├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── config.yml │ └── web_api_type_definition_issue.yml ├── codeql │ └── codeql-configuration.yml ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── codeowners-merge.yml │ ├── codeql.yml │ ├── deploy.yml │ ├── test_typescript.yml │ └── update-core-deps.yml ├── .gitignore ├── .gitmodules ├── LICENSE.txt ├── README.md ├── SECURITY.md ├── baselines ├── audioworklet.asynciterable.generated.d.ts ├── audioworklet.generated.d.ts ├── audioworklet.iterable.generated.d.ts ├── dom.asynciterable.generated.d.ts ├── dom.generated.d.ts ├── dom.iterable.generated.d.ts ├── serviceworker.asynciterable.generated.d.ts ├── serviceworker.generated.d.ts ├── serviceworker.iterable.generated.d.ts ├── sharedworker.asynciterable.generated.d.ts ├── sharedworker.generated.d.ts ├── sharedworker.iterable.generated.d.ts ├── ts5.5 │ ├── audioworklet.asynciterable.generated.d.ts │ ├── audioworklet.generated.d.ts │ ├── audioworklet.iterable.generated.d.ts │ ├── dom.asynciterable.generated.d.ts │ ├── dom.generated.d.ts │ ├── dom.iterable.generated.d.ts │ ├── serviceworker.asynciterable.generated.d.ts │ ├── serviceworker.generated.d.ts │ ├── serviceworker.iterable.generated.d.ts │ ├── sharedworker.asynciterable.generated.d.ts │ ├── sharedworker.generated.d.ts │ ├── sharedworker.iterable.generated.d.ts │ ├── webworker.asynciterable.generated.d.ts │ ├── webworker.generated.d.ts │ └── webworker.iterable.generated.d.ts ├── ts5.6 │ ├── audioworklet.asynciterable.generated.d.ts │ ├── audioworklet.generated.d.ts │ ├── audioworklet.iterable.generated.d.ts │ ├── dom.asynciterable.generated.d.ts │ ├── dom.generated.d.ts │ ├── dom.iterable.generated.d.ts │ ├── serviceworker.asynciterable.generated.d.ts │ ├── serviceworker.generated.d.ts │ ├── serviceworker.iterable.generated.d.ts │ ├── sharedworker.asynciterable.generated.d.ts │ ├── sharedworker.generated.d.ts │ ├── sharedworker.iterable.generated.d.ts │ ├── webworker.asynciterable.generated.d.ts │ ├── webworker.generated.d.ts │ └── webworker.iterable.generated.d.ts ├── webworker.asynciterable.generated.d.ts ├── webworker.generated.d.ts └── webworker.iterable.generated.d.ts ├── dangerfile.js ├── deploy ├── README.md ├── createTypesPackages.js ├── deployChangedPackages.js ├── jsconfig.json ├── migrate.js ├── onUpdateFailure.js ├── readmes │ ├── audioworklet.md │ ├── serviceworker.md │ ├── sharedworker.md │ ├── web.md │ └── webworker.md ├── template │ ├── LICENSE.txt │ └── package.json └── versionChangelog.js ├── eslint.config.mjs ├── inputfiles ├── README.md ├── addedTypes.jsonc ├── comments.json ├── deprecatedMessage.json ├── idl │ ├── IndexedDB.commentmap.json │ ├── dom.commentmap.json │ ├── encoding.commentmap.json │ ├── fetch.commentmap.json │ ├── fullscreen.commentmap.json │ ├── html.commentmap.json │ ├── streams.commentmap.json │ ├── websockets.commentmap.json │ └── xhr.commentmap.json ├── knownTypes.json ├── overridingTypes.jsonc └── removedTypes.jsonc ├── package-lock.json ├── package.json ├── src ├── build.ts ├── build │ ├── bcd.ts │ ├── bcd │ │ ├── keep-alive.ts │ │ ├── mapper.ts │ │ └── stable.ts │ ├── browser-specs.ts │ ├── emitter.ts │ ├── expose.ts │ ├── helpers.ts │ ├── legacy-namespace.ts │ ├── mdn-comments.ts │ ├── types.d.ts │ ├── utils │ │ ├── css.ts │ │ ├── fs.ts │ │ ├── map.ts │ │ └── record.ts │ ├── webref │ │ ├── css.ts │ │ ├── elements.ts │ │ ├── events.ts │ │ ├── idl.ts │ │ ├── webref-css.d.ts │ │ ├── webref-elements.d.ts │ │ ├── webref-events.d.ts │ │ └── webref-idl.d.ts │ └── widlprocess.ts ├── changelog.ts ├── test.ts └── version.ts ├── tsconfig.json └── unittests ├── files ├── audioworklet │ ├── tsconfig.json │ └── usage.ts ├── autocomplete.ts ├── childnode.ts ├── differingaccessors.ts ├── eventlistener.ts ├── eventsource.ts ├── formData.ts ├── htmldocument.ts ├── keyusage.ts ├── mathml.ts ├── streams.ts ├── structuredClone.ts ├── tsconfig.json └── url.ts ├── index.js └── jsconfig.json /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto eol=lf 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | src/**/*.ts @saschanaz 2 | baselines/* @saschanaz 3 | inputfiles/**/* @saschanaz 4 | unittests/**/* @saschanaz 5 | README.md @saschanaz 6 | package-lock.json @saschanaz 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | issue_templates: 3 | - name: Web API type definition issue 4 | description: Report inconsistencies between TypeScript's web API typings and browser behavior. 5 | labels: [lib.d.ts bug] 6 | file: web_api_type_definition_issue.yml 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/web_api_type_definition_issue.yml: -------------------------------------------------------------------------------- 1 | name: "Web API type definition issue" 2 | title: "[Web API type definition issue] " 3 | labels: [lib.d.ts bug] 4 | assignees: [] 5 | description: "Report inconsistencies between TypeScript's web API typings and browser behavior." 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | ## Web API Type Definition Issue 11 | Provide details of the issue. 12 | - type: input 13 | id: issue_summary 14 | attributes: 15 | label: "Summary" 16 | description: "Brief summary of the issue" 17 | placeholder: "e.g., Mismatch in event handling..." 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: expected_vs_actual 22 | attributes: 23 | label: "Expected vs. Actual Behavior" 24 | description: "Describe what you expected and what actually happened" 25 | placeholder: "Expected: ...\nActual: ..." 26 | validations: 27 | required: true 28 | - type: input 29 | id: playground_link 30 | attributes: 31 | label: "Playground Link" 32 | description: "Paste the TypeScript playground link" 33 | placeholder: "https://www.typescriptlang.org/play/..." 34 | - type: checkboxes 35 | id: browser_support 36 | attributes: 37 | label: "Browser Support" 38 | description: "Ensure that the API is supported in at least two major browser engines (not two Chromium-based browsers)." 39 | options: 40 | - label: "This API is supported in at least two major browser engines (not two Chromium-based browsers)." 41 | required: true 42 | - type: checkboxes 43 | id: tried_latest_releases 44 | attributes: 45 | label: "Have Tried The Latest Releases" 46 | description: "Make sure your problem is still reproducible on the latest releases." 47 | options: 48 | - label: "This issue applies to the latest release of TypeScript." 49 | required: true 50 | - label: "This issue applies to the latest release of `@types/web`." 51 | required: true 52 | - type: textarea 53 | id: additional_context 54 | attributes: 55 | label: "Additional Context" 56 | description: "Any extra information, logs, or references." 57 | placeholder: "Optional details..." 58 | -------------------------------------------------------------------------------- /.github/codeql/codeql-configuration.yml: -------------------------------------------------------------------------------- 1 | name : CodeQL Configuration 2 | 3 | paths: 4 | - './src' 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Basic dependabot.yml file 2 | # https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/enabling-and-disabling-version-updates 3 | 4 | version: 2 5 | updates: 6 | # Enable version updates for npm 7 | - package-ecosystem: "npm" 8 | # Look for `package.json` and `lock` files in the `root` directory 9 | directory: "/" 10 | # Check the npm registry for updates every month 11 | schedule: 12 | interval: "monthly" 13 | # Usually only bump versions in package-lock.json, but update package.json for major version updates 14 | versioning-strategy: "increase-if-necessary" 15 | # Update all dependencies in a single PR 16 | groups: 17 | # ESLint usually requires updating together for major updates 18 | eslint: 19 | patterns: 20 | - "eslint*" 21 | - "@typescript-eslint/*" 22 | # Other updates should be okay all at once 23 | dev-dependencies: 24 | patterns: 25 | - "*" 26 | exclude-patterns: 27 | - "@mdn/*" 28 | - "@webref/*" 29 | # Enable version updates for GitHub Actions 30 | - package-ecosystem: "github-actions" 31 | directory: "/" 32 | schedule: 33 | interval: "monthly" 34 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | test: 6 | runs-on: ubuntu-latest 7 | 8 | steps: 9 | - uses: actions/checkout@v4 10 | with: 11 | submodules: true # Ensures submodules are cloned 12 | 13 | 14 | - uses: actions/setup-node@v4 15 | with: 16 | node-version: "lts/*" 17 | cache: npm 18 | 19 | - run: npm ci 20 | - run: npm run build 21 | - run: npm test 22 | -------------------------------------------------------------------------------- /.github/workflows/codeowners-merge.yml: -------------------------------------------------------------------------------- 1 | name: Codeowners merging 2 | on: 3 | pull_request_target: { types: [opened] } 4 | issue_comment: { types: [created] } 5 | pull_request_review: { types: [submitted] } 6 | 7 | permissions: 8 | contents: write 9 | issues: write 10 | pull-requests: write 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: Run Codeowners merge check 19 | uses: OSS-Docs-Tools/code-owner-self-merge@1.6.8 20 | if: github.repository == 'microsoft/TypeScript-DOM-lib-generator' 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | with: 24 | merge_method: 'squash' 25 | if_no_maintainers_add_label: 'maintainers' 26 | if_no_maintainers_assign: '@sandersn @jakebailey' 27 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: 'Code Scanning - Action' 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | schedule: 11 | # ┌───────────── minute (0 - 59) 12 | # │ ┌───────────── hour (0 - 23) 13 | # │ │ ┌───────────── day of the month (1 - 31) 14 | # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) 15 | # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) 16 | # │ │ │ │ │ 17 | # │ │ │ │ │ 18 | # │ │ │ │ │ 19 | # * * * * * 20 | - cron: '30 1 * * 0' 21 | 22 | permissions: 23 | contents: read 24 | 25 | # Ensure scripts are run with pipefail. See: 26 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference 27 | defaults: 28 | run: 29 | shell: bash 30 | 31 | jobs: 32 | CodeQL-Build: 33 | # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest 34 | runs-on: ubuntu-latest 35 | if: github.repository == 'microsoft/TypeScript-DOM-lib-generator' 36 | 37 | permissions: 38 | # required for all workflows 39 | security-events: write 40 | 41 | steps: 42 | - name: Checkout repository 43 | uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 44 | 45 | # Initializes the CodeQL tools for scanning. 46 | - name: Initialize CodeQL 47 | uses: github/codeql-action/init@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.13 48 | with: 49 | config-file: ./.github/codeql/codeql-configuration.yml 50 | # Override language selection by uncommenting this and choosing your languages 51 | # with: 52 | # languages: go, javascript, csharp, python, cpp, java 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below). 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.13 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following 63 | # three lines and modify them (or add more) to build your code if your 64 | # project uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.13 -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to npm 2 | 3 | on: 4 | schedule: 5 | # Daily run 6 | - cron: "0 4 * * *" 7 | workflow_dispatch: null 8 | 9 | permissions: 10 | contents: write 11 | 12 | jobs: 13 | deploy: 14 | if: github.repository == 'microsoft/TypeScript-DOM-lib-generator' 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | submodules: true # Ensures submodules are cloned 21 | 22 | - uses: actions/setup-node@v4 23 | with: 24 | node-version: "lts/*" 25 | cache: npm 26 | 27 | - run: git pull --tags 28 | - run: npm install 29 | - run: npm run build 30 | - run: npm test 31 | 32 | - name: Create packages for .d.ts files 33 | run: node deploy/createTypesPackages.js 34 | 35 | - name: "Deploy built packages to NPM" 36 | run: | 37 | echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc 38 | npm i -g npm@latest 39 | npm whoami 40 | node deploy/deployChangedPackages.js 41 | env: 42 | NPM_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} 43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 44 | -------------------------------------------------------------------------------- /.github/workflows/test_typescript.yml: -------------------------------------------------------------------------------- 1 | name: Runs with TypeScript Tests 2 | on: 3 | push: 4 | paths: 5 | - "baselines/*" 6 | pull_request: 7 | paths: 8 | - "baselines/*" 9 | 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | submodules: true # Ensures submodules are cloned 19 | 20 | 21 | - uses: actions/setup-node@v4 22 | with: 23 | node-version: "lts/*" 24 | cache: npm 25 | 26 | - name: Run TypeScript Compiler Tests with new dom.d.ts 27 | run: | 28 | # Get local dependencies 29 | npm install 30 | # Make our new dom APIs 31 | npm run build 32 | 33 | # Clone TypeScript, set it up 34 | git clone https://github.com/microsoft/TypeScript --depth 1 35 | 36 | # Migrate the generated files into the repo 37 | npm run migrate 38 | 39 | cd TypeScript 40 | npm i 41 | 42 | 43 | # Run TypeScript's tests with the new DOM libs, but don't fail 44 | npm test || true 45 | gulp baseline-accept 46 | 47 | # The git diff now is the difference between tests 48 | git diff > baseline-changes.diff 49 | 50 | - name: Danger 51 | run: npx danger -- ci 52 | env: 53 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 54 | -------------------------------------------------------------------------------- /.github/workflows/update-core-deps.yml: -------------------------------------------------------------------------------- 1 | name: Update core dependencies 2 | on: 3 | workflow_dispatch: null 4 | schedule: 5 | # https://crontab.guru/#5_8_*_*_* 6 | - cron: "5 8 * * *" 7 | 8 | # Conditional runs 9 | # https://stackoverflow.com/a/61832535/2460034 10 | jobs: 11 | update-webref: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | submodules: true # Ensure submodules are checked out 17 | 18 | - name: Update submodules 19 | run: git submodule update --init --remote 20 | - uses: actions/setup-node@v4 21 | with: 22 | node-version: "lts/*" 23 | cache: npm 24 | 25 | # Use ncu to detect major version changes 26 | - run: npm i -g npm-check-updates 27 | - run: ncu -u @mdn* @webref* 28 | - run: npm i 29 | - run: git restore package.json 30 | # package-lock wants to remember the original version numbers of package.json 31 | # This prevents annoying change when contributors run `npm i` on their local machine. 32 | # Example: https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1463 33 | - run: npm i 34 | - id: build 35 | run: npm run generate 36 | continue-on-error: true 37 | - if: ${{ steps.build.outcome == 'failure' }} 38 | run: node deploy/onUpdateFailure.js 39 | env: 40 | GITHUB_TOKEN: ${{ secrets.TS_GITHUB_BOT_AUTH }} 41 | - id: git-diff 42 | run: git diff --quiet HEAD baselines 43 | continue-on-error: true 44 | - uses: peter-evans/create-pull-request@v7 45 | if: ${{ steps.git-diff.outcome == 'failure' }} 46 | with: 47 | commit-message: "🤖 Update core dependencies" 48 | title: "Update core dependencies" 49 | push-to-fork: typescript-bot/TypeScript-DOM-lib-generator 50 | branch: update-core-deps 51 | token: ${{ secrets.TS_GITHUB_BOT_AUTH }} 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | generated/ 282 | lib/ 283 | inputfiles/browser.webidl.json 284 | .vscode/* 285 | !.vscode/launch.template.json 286 | yarn.lock 287 | TypeScript 288 | deploy/generated -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "inputfiles/mdn"] 2 | path = inputfiles/mdn 3 | url = https://github.com/mdn/content.git 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 16 | 17 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 18 | 19 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 20 | 21 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 22 | 23 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 24 | 25 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 26 | 27 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 28 | 29 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 30 | 31 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 32 | 33 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 34 | 35 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 36 | 37 | You must give any other recipients of the Work or Derivative Works a copy of this License; and 38 | 39 | You must cause any modified files to carry prominent notices stating that You changed the files; and 40 | 41 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 42 | 43 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 44 | 45 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 46 | 47 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 48 | 49 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 50 | 51 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 52 | 53 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 54 | 55 | END OF TERMS AND CONDITIONS 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TypeScript and JavaScript lib generator 2 | 3 | This tool is used to generate the web-based `lib.dom.d.ts` file which is included with TypeScript releases, and as the `@types/web` package. 4 | 5 | ## Why is my fancy API still not available here? 6 | 7 | A feature needs to be supported by two or more major browser engines to be included here, to make sure there is a good consensus among vendors: __Gecko__ (Firefox), __Blink__ (Chrome/Edge), and __WebKit__ (Safari). 8 | 9 | If the condition is met but still is not available here, first check the [contribution guidelines](#contribution-guidelines) below and then please [file an issue](https://github.com/microsoft/TypeScript-DOM-lib-generator/issues/new). 10 | 11 | ## Build Instructions 12 | 13 | To get things setup: 14 | 15 | ```sh 16 | npm install 17 | ``` 18 | 19 | To generate the `.d.ts` files 20 | 21 | ```sh 22 | npm run build 23 | ``` 24 | 25 | To test: 26 | 27 | ```sh 28 | npm run test 29 | ``` 30 | 31 | 32 | ## `@types/[lib]` to TypeScript Versions 33 | 34 | | `@types/[lib]` version | TypeScript Version | Minimum TypeScript Support | 35 | | ------------------------------------------------------------------------- | ----------- | -------------- | 36 | | `@types/web` [0.0.1](https://www.npmjs.com/package/@types/web/v/0.0.1) | ~4.3 | 4.4 | 37 | | `@types/web` [0.0.2](https://www.npmjs.com/package/@types/web/v/0.0.2) | ~4.4 beta | 4.4 | 38 | | `@types/web` [0.0.25](https://www.npmjs.com/package/@types/web/v/0.0.25) | 4.4 | 4.4 | 39 | | `@types/web` [0.0.28](https://www.npmjs.com/package/@types/web/v/0.0.28) | 4.5 beta | 4.4 | 40 | | `@types/web` [0.0.37](https://www.npmjs.com/package/@types/web/v/0.0.37) | 4.5 rc | 4.4 | 41 | | `@types/web` [0.0.37](https://www.npmjs.com/package/@types/web/v/0.0.37) | 4.5 | 4.4 | 42 | | `@types/web` [0.0.50](https://www.npmjs.com/package/@types/web/v/0.0.50) | 4.6 beta | 4.4 | 43 | | `@types/web` [0.0.51](https://www.npmjs.com/package/@types/web/v/0.0.51) | 4.6 rc | 4.4 | 44 | | `@types/web` [0.0.51](https://www.npmjs.com/package/@types/web/v/0.0.51) | 4.6 | 4.4 | 45 | | `@types/web` [0.0.61](https://www.npmjs.com/package/@types/web/v/0.0.61) | 4.7 beta | 4.4 | 46 | | `@types/web` [0.0.61](https://www.npmjs.com/package/@types/web/v/0.0.61) | 4.7 rc | 4.4 | 47 | | `@types/web` [0.0.61](https://www.npmjs.com/package/@types/web/v/0.0.61) | 4.7 | 4.4 | 48 | | `@types/web` [0.0.68](https://www.npmjs.com/package/@types/web/v/0.0.68) | 4.8 beta | 4.4 | 49 | | `@types/web` [0.0.69](https://www.npmjs.com/package/@types/web/v/0.0.69) | 4.8 rc | 4.4 | 50 | | `@types/web` [0.0.69](https://www.npmjs.com/package/@types/web/v/0.0.69) | 4.8 | 4.4 | 51 | | `@types/web` [0.0.76](https://www.npmjs.com/package/@types/web/v/0.0.76) | 4.9 | 4.4 | 52 | 53 | ## `@types/[lib]` Minimum Target 54 | 55 | The libraries available on `@types/` like `@types/web` require a [`"target"`](https://www.typescriptlang.org/tsconfig#target) of ES6 or above, because [iterator](https://www.typescriptlang.org/docs/handbook/iterators-and-generators.html) APIs are included. 56 | 57 | ## Contribution Guidelines 58 | 59 | The files in the `baselines/` directory from the TypeScript repo are used as baselines. 60 | For each pull request, we will run the script and compare the generated files with the baseline files. 61 | In order to make the tests pass, please update the baseline as well in any pull requests. 62 | 63 | ### When the type is missing 64 | 65 | It's possible that the automated algorithm decided that it's not well supported by browsers and thus removed it. Say we want to add a new interface named `Foo`. Check if there is a document about that interface in [MDN](https://developer.mozilla.org/). If there is, see the browser compatibility section and check whether it's supported by two or more browser engines. (Note that Chromium-based browsers use the same browser engine and thus support from them counts as a single support.) 66 | 67 | If all the conditions are fulfilled, it could be that the type is incorrectly removed by `inputfiles/removedTypes.jsonc`. Try finding and removing the relevant item there and run `npm run generate`. 68 | 69 | If conditions are not fulfilled but you think MDN is wrong, please file an issue at https://github.com/mdn/browser-compat-data/issues/. The type will be automatically added in a few weeks when MDN fixes their data. 70 | 71 | ### When the type exists but is wrong 72 | 73 | It's possible that the type is too specific or too general. First you need to check whether `inputfiles/overridingTypes.jsonc` or `inputfiles/addedTypes.jsonc` have a relevant item, which can be fixed if exists. If they don't, add one in `overridingTypes.jsonc`. Run `npm run generate` to make sure the resulting changes are what you want. 74 | 75 | If you are familiar with Web IDL, you may also want to check whether the upstream IDL itself can be made more specific. Doing so will reduce the need for manual overrides in this repository and thus can be more helpful. 76 | 77 | # This repo 78 | 79 | ## Code Structure 80 | 81 | - `src/build.ts`: handles the emitting of the `.d.ts` files. 82 | - `src/test.ts`: verifies the output by comparing the `generated/` and `baseline/` contents. 83 | 84 | ## Input Files 85 | 86 | - `addedTypes.jsonc`: types that should exist but are missing from the spec data. 87 | - `overridingTypes.jsonc`: types that are defined in the spec but have TypeScript-friendly modifications in the json files. 88 | - `removedTypes.jsonc`: types that are defined in the spec but should be removed. 89 | - `comments.json`: comment strings to be embedded in the generated .d.ts files. 90 | - `deprecatedMessage.json`: the reason why one type is deprecated. 91 | 92 | ## Deployment to TypeScript 93 | 94 | To migrate the *.d.ts files into TypeScript: 95 | 96 | 1. [Trigger the workflow here](https://github.com/microsoft/TypeScript-DOM-lib-generator/actions/workflows/pr-to-typescript.yml) - this will send a PR to TypeScript under your alias. 97 | 98 | 99 | 1. Update the README table with the mappings for versions in the `@types/[lib]`. E.g. TS 4.5 -> `@types/web` `0.0.23`. Find that number here: https://www.npmjs.com/package/@types/web 100 | 101 | 1. Generate a CHANGELOG for the releases: 102 | 103 | ```sh 104 | # lib from to 105 | npm run ts-changelog -- @types/web 0.0.2 0.0.23 106 | ``` 107 | 108 | You might need to run `git pull origin main --tags` to run this ^ 109 | 110 | 1. Add the CHANGELOG to the release issue 111 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /baselines/audioworklet.asynciterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// AudioWorklet Async Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface ReadableStreamAsyncIterator extends AsyncIteratorObject { 6 | [Symbol.asyncIterator](): ReadableStreamAsyncIterator; 7 | } 8 | 9 | interface ReadableStream { 10 | [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 11 | values(options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 12 | } 13 | -------------------------------------------------------------------------------- /baselines/audioworklet.iterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// AudioWorklet Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface MessageEvent { 6 | /** @deprecated */ 7 | initMessageEvent(type: string, bubbles?: boolean, cancelable?: boolean, data?: any, origin?: string, lastEventId?: string, source?: MessageEventSource | null, ports?: Iterable): void; 8 | } 9 | 10 | interface URLSearchParamsIterator extends IteratorObject { 11 | [Symbol.iterator](): URLSearchParamsIterator; 12 | } 13 | 14 | interface URLSearchParams { 15 | [Symbol.iterator](): URLSearchParamsIterator<[string, string]>; 16 | /** Returns an array of key, value pairs for every entry in the search params. */ 17 | entries(): URLSearchParamsIterator<[string, string]>; 18 | /** Returns a list of keys in the search params. */ 19 | keys(): URLSearchParamsIterator; 20 | /** Returns a list of values in the search params. */ 21 | values(): URLSearchParamsIterator; 22 | } 23 | -------------------------------------------------------------------------------- /baselines/dom.asynciterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// Window Async Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface FileSystemDirectoryHandleAsyncIterator extends AsyncIteratorObject { 6 | [Symbol.asyncIterator](): FileSystemDirectoryHandleAsyncIterator; 7 | } 8 | 9 | interface FileSystemDirectoryHandle { 10 | [Symbol.asyncIterator](): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemHandle]>; 11 | entries(): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemHandle]>; 12 | keys(): FileSystemDirectoryHandleAsyncIterator; 13 | values(): FileSystemDirectoryHandleAsyncIterator; 14 | } 15 | 16 | interface ReadableStreamAsyncIterator extends AsyncIteratorObject { 17 | [Symbol.asyncIterator](): ReadableStreamAsyncIterator; 18 | } 19 | 20 | interface ReadableStream { 21 | [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 22 | values(options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 23 | } 24 | -------------------------------------------------------------------------------- /baselines/serviceworker.asynciterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// ServiceWorker Async Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface FileSystemDirectoryHandleAsyncIterator extends AsyncIteratorObject { 6 | [Symbol.asyncIterator](): FileSystemDirectoryHandleAsyncIterator; 7 | } 8 | 9 | interface FileSystemDirectoryHandle { 10 | [Symbol.asyncIterator](): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemHandle]>; 11 | entries(): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemHandle]>; 12 | keys(): FileSystemDirectoryHandleAsyncIterator; 13 | values(): FileSystemDirectoryHandleAsyncIterator; 14 | } 15 | 16 | interface ReadableStreamAsyncIterator extends AsyncIteratorObject { 17 | [Symbol.asyncIterator](): ReadableStreamAsyncIterator; 18 | } 19 | 20 | interface ReadableStream { 21 | [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 22 | values(options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 23 | } 24 | -------------------------------------------------------------------------------- /baselines/sharedworker.asynciterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// SharedWorker Async Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface FileSystemDirectoryHandleAsyncIterator extends AsyncIteratorObject { 6 | [Symbol.asyncIterator](): FileSystemDirectoryHandleAsyncIterator; 7 | } 8 | 9 | interface FileSystemDirectoryHandle { 10 | [Symbol.asyncIterator](): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemHandle]>; 11 | entries(): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemHandle]>; 12 | keys(): FileSystemDirectoryHandleAsyncIterator; 13 | values(): FileSystemDirectoryHandleAsyncIterator; 14 | } 15 | 16 | interface ReadableStreamAsyncIterator extends AsyncIteratorObject { 17 | [Symbol.asyncIterator](): ReadableStreamAsyncIterator; 18 | } 19 | 20 | interface ReadableStream { 21 | [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 22 | values(options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 23 | } 24 | -------------------------------------------------------------------------------- /baselines/ts5.5/audioworklet.asynciterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// AudioWorklet Async Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface ReadableStream { 6 | [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): AsyncIterableIterator; 7 | values(options?: ReadableStreamIteratorOptions): AsyncIterableIterator; 8 | } 9 | -------------------------------------------------------------------------------- /baselines/ts5.5/audioworklet.iterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// AudioWorklet Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface MessageEvent { 6 | /** @deprecated */ 7 | initMessageEvent(type: string, bubbles?: boolean, cancelable?: boolean, data?: any, origin?: string, lastEventId?: string, source?: MessageEventSource | null, ports?: Iterable): void; 8 | } 9 | 10 | interface URLSearchParams { 11 | [Symbol.iterator](): IterableIterator<[string, string]>; 12 | /** Returns an array of key, value pairs for every entry in the search params. */ 13 | entries(): IterableIterator<[string, string]>; 14 | /** Returns a list of keys in the search params. */ 15 | keys(): IterableIterator; 16 | /** Returns a list of values in the search params. */ 17 | values(): IterableIterator; 18 | } 19 | -------------------------------------------------------------------------------- /baselines/ts5.5/dom.asynciterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// Window Async Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface FileSystemDirectoryHandle { 6 | [Symbol.asyncIterator](): AsyncIterableIterator<[string, FileSystemHandle]>; 7 | entries(): AsyncIterableIterator<[string, FileSystemHandle]>; 8 | keys(): AsyncIterableIterator; 9 | values(): AsyncIterableIterator; 10 | } 11 | 12 | interface ReadableStream { 13 | [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): AsyncIterableIterator; 14 | values(options?: ReadableStreamIteratorOptions): AsyncIterableIterator; 15 | } 16 | -------------------------------------------------------------------------------- /baselines/ts5.5/serviceworker.asynciterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// ServiceWorker Async Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface FileSystemDirectoryHandle { 6 | [Symbol.asyncIterator](): AsyncIterableIterator<[string, FileSystemHandle]>; 7 | entries(): AsyncIterableIterator<[string, FileSystemHandle]>; 8 | keys(): AsyncIterableIterator; 9 | values(): AsyncIterableIterator; 10 | } 11 | 12 | interface ReadableStream { 13 | [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): AsyncIterableIterator; 14 | values(options?: ReadableStreamIteratorOptions): AsyncIterableIterator; 15 | } 16 | -------------------------------------------------------------------------------- /baselines/ts5.5/sharedworker.asynciterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// SharedWorker Async Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface FileSystemDirectoryHandle { 6 | [Symbol.asyncIterator](): AsyncIterableIterator<[string, FileSystemHandle]>; 7 | entries(): AsyncIterableIterator<[string, FileSystemHandle]>; 8 | keys(): AsyncIterableIterator; 9 | values(): AsyncIterableIterator; 10 | } 11 | 12 | interface ReadableStream { 13 | [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): AsyncIterableIterator; 14 | values(options?: ReadableStreamIteratorOptions): AsyncIterableIterator; 15 | } 16 | -------------------------------------------------------------------------------- /baselines/ts5.5/webworker.asynciterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// Worker Async Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface FileSystemDirectoryHandle { 6 | [Symbol.asyncIterator](): AsyncIterableIterator<[string, FileSystemHandle]>; 7 | entries(): AsyncIterableIterator<[string, FileSystemHandle]>; 8 | keys(): AsyncIterableIterator; 9 | values(): AsyncIterableIterator; 10 | } 11 | 12 | interface ReadableStream { 13 | [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): AsyncIterableIterator; 14 | values(options?: ReadableStreamIteratorOptions): AsyncIterableIterator; 15 | } 16 | -------------------------------------------------------------------------------- /baselines/ts5.6/audioworklet.asynciterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// AudioWorklet Async Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface ReadableStreamAsyncIterator extends AsyncIteratorObject { 6 | [Symbol.asyncIterator](): ReadableStreamAsyncIterator; 7 | } 8 | 9 | interface ReadableStream { 10 | [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 11 | values(options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 12 | } 13 | -------------------------------------------------------------------------------- /baselines/ts5.6/audioworklet.iterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// AudioWorklet Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface MessageEvent { 6 | /** @deprecated */ 7 | initMessageEvent(type: string, bubbles?: boolean, cancelable?: boolean, data?: any, origin?: string, lastEventId?: string, source?: MessageEventSource | null, ports?: Iterable): void; 8 | } 9 | 10 | interface URLSearchParamsIterator extends IteratorObject { 11 | [Symbol.iterator](): URLSearchParamsIterator; 12 | } 13 | 14 | interface URLSearchParams { 15 | [Symbol.iterator](): URLSearchParamsIterator<[string, string]>; 16 | /** Returns an array of key, value pairs for every entry in the search params. */ 17 | entries(): URLSearchParamsIterator<[string, string]>; 18 | /** Returns a list of keys in the search params. */ 19 | keys(): URLSearchParamsIterator; 20 | /** Returns a list of values in the search params. */ 21 | values(): URLSearchParamsIterator; 22 | } 23 | -------------------------------------------------------------------------------- /baselines/ts5.6/dom.asynciterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// Window Async Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface FileSystemDirectoryHandleAsyncIterator extends AsyncIteratorObject { 6 | [Symbol.asyncIterator](): FileSystemDirectoryHandleAsyncIterator; 7 | } 8 | 9 | interface FileSystemDirectoryHandle { 10 | [Symbol.asyncIterator](): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemHandle]>; 11 | entries(): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemHandle]>; 12 | keys(): FileSystemDirectoryHandleAsyncIterator; 13 | values(): FileSystemDirectoryHandleAsyncIterator; 14 | } 15 | 16 | interface ReadableStreamAsyncIterator extends AsyncIteratorObject { 17 | [Symbol.asyncIterator](): ReadableStreamAsyncIterator; 18 | } 19 | 20 | interface ReadableStream { 21 | [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 22 | values(options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 23 | } 24 | -------------------------------------------------------------------------------- /baselines/ts5.6/serviceworker.asynciterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// ServiceWorker Async Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface FileSystemDirectoryHandleAsyncIterator extends AsyncIteratorObject { 6 | [Symbol.asyncIterator](): FileSystemDirectoryHandleAsyncIterator; 7 | } 8 | 9 | interface FileSystemDirectoryHandle { 10 | [Symbol.asyncIterator](): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemHandle]>; 11 | entries(): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemHandle]>; 12 | keys(): FileSystemDirectoryHandleAsyncIterator; 13 | values(): FileSystemDirectoryHandleAsyncIterator; 14 | } 15 | 16 | interface ReadableStreamAsyncIterator extends AsyncIteratorObject { 17 | [Symbol.asyncIterator](): ReadableStreamAsyncIterator; 18 | } 19 | 20 | interface ReadableStream { 21 | [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 22 | values(options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 23 | } 24 | -------------------------------------------------------------------------------- /baselines/ts5.6/sharedworker.asynciterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// SharedWorker Async Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface FileSystemDirectoryHandleAsyncIterator extends AsyncIteratorObject { 6 | [Symbol.asyncIterator](): FileSystemDirectoryHandleAsyncIterator; 7 | } 8 | 9 | interface FileSystemDirectoryHandle { 10 | [Symbol.asyncIterator](): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemHandle]>; 11 | entries(): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemHandle]>; 12 | keys(): FileSystemDirectoryHandleAsyncIterator; 13 | values(): FileSystemDirectoryHandleAsyncIterator; 14 | } 15 | 16 | interface ReadableStreamAsyncIterator extends AsyncIteratorObject { 17 | [Symbol.asyncIterator](): ReadableStreamAsyncIterator; 18 | } 19 | 20 | interface ReadableStream { 21 | [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 22 | values(options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 23 | } 24 | -------------------------------------------------------------------------------- /baselines/ts5.6/webworker.asynciterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// Worker Async Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface FileSystemDirectoryHandleAsyncIterator extends AsyncIteratorObject { 6 | [Symbol.asyncIterator](): FileSystemDirectoryHandleAsyncIterator; 7 | } 8 | 9 | interface FileSystemDirectoryHandle { 10 | [Symbol.asyncIterator](): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemHandle]>; 11 | entries(): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemHandle]>; 12 | keys(): FileSystemDirectoryHandleAsyncIterator; 13 | values(): FileSystemDirectoryHandleAsyncIterator; 14 | } 15 | 16 | interface ReadableStreamAsyncIterator extends AsyncIteratorObject { 17 | [Symbol.asyncIterator](): ReadableStreamAsyncIterator; 18 | } 19 | 20 | interface ReadableStream { 21 | [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 22 | values(options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 23 | } 24 | -------------------------------------------------------------------------------- /baselines/webworker.asynciterable.generated.d.ts: -------------------------------------------------------------------------------- 1 | ///////////////////////////// 2 | /// Worker Async Iterable APIs 3 | ///////////////////////////// 4 | 5 | interface FileSystemDirectoryHandleAsyncIterator extends AsyncIteratorObject { 6 | [Symbol.asyncIterator](): FileSystemDirectoryHandleAsyncIterator; 7 | } 8 | 9 | interface FileSystemDirectoryHandle { 10 | [Symbol.asyncIterator](): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemHandle]>; 11 | entries(): FileSystemDirectoryHandleAsyncIterator<[string, FileSystemHandle]>; 12 | keys(): FileSystemDirectoryHandleAsyncIterator; 13 | values(): FileSystemDirectoryHandleAsyncIterator; 14 | } 15 | 16 | interface ReadableStreamAsyncIterator extends AsyncIteratorObject { 17 | [Symbol.asyncIterator](): ReadableStreamAsyncIterator; 18 | } 19 | 20 | interface ReadableStream { 21 | [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 22 | values(options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; 23 | } 24 | -------------------------------------------------------------------------------- /dangerfile.js: -------------------------------------------------------------------------------- 1 | const {markdown} = require("danger") 2 | const {readFileSync, existsSync} = require("fs") 3 | const parseDiff = require("parse-diff") 4 | 5 | const diffPath = "./TypeScript/baseline-changes.diff" 6 | if (existsSync(diffPath)) { 7 | const diffContents = readFileSync(diffPath, "utf8") 8 | const diffedFiles = parseDiff(diffContents) 9 | 10 | const uninterestingFiles = [".generated.d.ts", "globalThisBlockscopedProperties.types", "mappedTypeRecursiveInference.types"] 11 | const withoutKnownNormalFails = diffedFiles.filter(diff => { 12 | return !uninterestingFiles.filter(suffix => diff.to && diff.to.endsWith(suffix)).length > 0 13 | }) 14 | 15 | const md = ["## Changed baselines from the TypeScript test suite", "\nThese are the test changes in the TypeScript codebase which showed a difference (excluding a few which will always change), it should give a small sense of what to expect on the TypeScript side if this PR is merged."] 16 | 17 | withoutKnownNormalFails.forEach(diff => { 18 | md.push(`#### [${diff.to || diff.from}](https://github.com/microsoft/TypeScript/blob/master/${diff.to || diff.from})`) 19 | 20 | md.push("```diff") 21 | diff.chunks.forEach(chunk => { 22 | md.push(chunk.content) 23 | 24 | chunk.changes.forEach(change => { 25 | md.push(change.content) 26 | }) 27 | }) 28 | md.push("```") 29 | }) 30 | 31 | if (md.length > 2) { 32 | markdown(md.join("\n")) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /deploy/README.md: -------------------------------------------------------------------------------- 1 | ## Deploys 2 | 3 | We want to take the `d.ts` files inside `generated` into a set of different `@types` packages. This infra all lives inside these files as multiple steps. For debugging you mostly want to run: 4 | 5 | ```sh 6 | node deploy/createTypesPackages.js 7 | ``` 8 | 9 | Then look at `deploy/generated` to see the set of NPM packages. -------------------------------------------------------------------------------- /deploy/deployChangedPackages.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | // node deploy/deployChangedPackages.js 4 | 5 | // Builds on the results of createTypesPackages.js and deploys the 6 | // ones which have changed. 7 | 8 | import * as fs from "fs"; 9 | import { spawnSync } from "child_process"; 10 | import { Octokit } from "@octokit/rest"; 11 | import { printUnifiedDiff } from "print-diff"; 12 | import { generateChangelogFrom } from "../lib/changelog.js"; 13 | import { packages } from "./createTypesPackages.js"; 14 | import { fileURLToPath } from "node:url"; 15 | import fetch from "node-fetch"; 16 | 17 | verify(); 18 | 19 | const uploaded = []; 20 | 21 | // Loop through generated packages, deploying versions for anything which has different 22 | // .d.ts files from the version available on npm. 23 | const generatedDir = new URL("generated/", import.meta.url); 24 | for (const dirName of fs.readdirSync(generatedDir)) { 25 | const packageDir = new URL(`${dirName}/`, generatedDir); 26 | const localPackageJSONPath = new URL("package.json", packageDir); 27 | const newTSConfig = fs.readFileSync(localPackageJSONPath, "utf-8"); 28 | const pkgJSON = JSON.parse(newTSConfig); 29 | 30 | // This assumes we'll only _ever_ ship patches, which may change in the 31 | // future someday. 32 | const [maj, min, patch] = pkgJSON.version.split("."); 33 | const olderVersion = `${maj}.${min}.${patch - 1}`; 34 | 35 | console.log(`\nLooking at ${dirName} vs ${olderVersion}`); 36 | 37 | // We'll need to map back from the filename in the npm package to the 38 | // generated file in baselines inside the git tag 39 | const thisPackageMeta = packages.find((p) => p.name === pkgJSON.name); 40 | if (!thisPackageMeta) { 41 | throw new Error(`Couldn't find ${pkgJSON.name}`); 42 | } 43 | 44 | const dtsFiles = fs 45 | .readdirSync(packageDir) 46 | .filter((f) => f.endsWith(".d.ts")); 47 | 48 | const releaseNotes = []; 49 | 50 | // Look through each .d.ts file included in a package to 51 | // determine if anything has changed 52 | let upload = false; 53 | for (const file of dtsFiles) { 54 | const filemap = thisPackageMeta.files.find((f) => f.to === file); 55 | if (!filemap) { 56 | throw new Error(`Couldn't find ${file} from ${pkgJSON.name}`); 57 | } 58 | 59 | const generatedDTSPath = new URL(file, packageDir); 60 | const generatedDTSContent = fs.readFileSync(generatedDTSPath, "utf8"); 61 | 62 | try { 63 | const oldFile = await getFileFromUnpkg( 64 | `${pkgJSON.name}@${olderVersion}/${filemap.to}`, 65 | ); 66 | console.log(` - ${file}`); 67 | if (oldFile !== generatedDTSContent) 68 | printUnifiedDiff(oldFile, generatedDTSContent); 69 | 70 | const title = `## \`${file}\``; 71 | const notes = generateChangelogFrom(oldFile, generatedDTSContent); 72 | releaseNotes.push(title); 73 | releaseNotes.push(notes.trim() === "" ? "No changes" : notes); 74 | 75 | upload = upload || oldFile !== generatedDTSContent; 76 | } catch (error) { 77 | // Could not find a previous build 78 | console.log(` 79 | Could not get the file ${file} inside the npm package ${pkgJSON.name} from tag ${olderVersion}. 80 | Assuming that this means we need to upload this package.`); 81 | console.error(error); 82 | upload = true; 83 | } 84 | } 85 | 86 | // Publish via npm 87 | if (upload) { 88 | if (process.env.CI) { 89 | const publish = spawnSync("npm", ["publish", "--access", "public"], { 90 | cwd: fileURLToPath(packageDir), 91 | stdio: "inherit", 92 | }); 93 | 94 | if (publish.status) { 95 | console.log(publish.stdout?.toString()); 96 | console.log(publish.stderr?.toString()); 97 | process.exit(publish.status); 98 | } else { 99 | console.log(publish.stdout?.toString()); 100 | 101 | await createRelease( 102 | `${pkgJSON.name}@${pkgJSON.version}`, 103 | releaseNotes.join("\n\n"), 104 | ); 105 | } 106 | } else { 107 | console.log( 108 | "Wanting to run: 'npm publish --access public' in " + 109 | fileURLToPath(packageDir), 110 | ); 111 | } 112 | 113 | uploaded.push(dirName); 114 | 115 | console.log("\n# Release notes:\n"); 116 | console.log(releaseNotes.join("\n\n"), "\n\n"); 117 | } 118 | } 119 | console.log(""); 120 | 121 | // Warn if we did a dry run. 122 | if (!process.env.CI) { 123 | console.log("Did a dry run because process.env.CI is not set."); 124 | } 125 | 126 | if (uploaded.length) { 127 | console.log("Uploaded: ", uploaded.join(", ")); 128 | } else { 129 | console.log("Nothing to upload"); 130 | } 131 | 132 | /** 133 | * @param {string} tag 134 | * @param {string} body 135 | */ 136 | async function createRelease(tag, body) { 137 | const authToken = process.env.GITHUB_TOKEN || process.env.GITHUB_API_TOKEN; 138 | const octokit = new Octokit({ auth: authToken }); 139 | 140 | try { 141 | await octokit.repos.createRelease({ 142 | owner: "microsoft", 143 | repo: "TypeScript-DOM-lib-generator", 144 | tag_name: tag, 145 | target_commitish: process.env.GITHUB_SHA, 146 | name: tag, 147 | body, 148 | }); 149 | } catch { 150 | console.error( 151 | "Creating the GitHub release failed, this is likely due to re-running the deploy.", 152 | ); 153 | } 154 | } 155 | 156 | function verify() { 157 | const authToken = process.env.GITHUB_TOKEN || process.env.GITHUB_API_TOKEN; 158 | if (!authToken) { 159 | throw new Error( 160 | "There isn't an ENV var set up for creating a GitHub release, expected GITHUB_TOKEN.", 161 | ); 162 | } 163 | } 164 | 165 | /** @param {string} filepath */ 166 | function getFileFromUnpkg(filepath) { 167 | return fetch(`https://unpkg.com/${filepath}`).then((r) => r.text()); 168 | } 169 | -------------------------------------------------------------------------------- /deploy/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "esnext", // es2022 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "resolveJsonModule": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /deploy/migrate.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Mainly a quick script to migrate the generated files into the 3 | // lib folder of a TypeScript clone. 4 | // 5 | // node ./deploy/migrate.js [optional/file/path/to/tsc] 6 | 7 | import { existsSync, readdirSync, readFileSync, writeFileSync } from "fs"; 8 | import { join } from "path"; 9 | 10 | import { postProcessDTSFiles } from "./createTypesPackages.js"; 11 | 12 | const maybeTSWorkingDir = [process.argv[2], "../TypeScript", "TypeScript"]; 13 | const tscWD = maybeTSWorkingDir.find((wd) => existsSync(wd)); 14 | 15 | if (!tscWD) 16 | throw new Error( 17 | "Could not find a TypeScript clone to put the generated files in.", 18 | ); 19 | 20 | const generatedFiles = readdirSync("generated"); 21 | const filesToSend = generatedFiles.filter( 22 | (file) => file.includes("dom.") || file.includes("webworker."), 23 | ); 24 | 25 | const generatedDir = new URL("../generated/", import.meta.url); 26 | postProcessDTSFiles( 27 | /** @type {any} */ 28 | ({ files: filesToSend.map((f) => ({ to: f })) }), 29 | generatedDir, 30 | ); 31 | 32 | filesToSend.forEach((file) => { 33 | const contents = readFileSync(join("generated", file), "utf8"); 34 | const newFilePath = join(tscWD, "src", "lib", file); 35 | writeFileSync(newFilePath, contents); 36 | }); 37 | 38 | console.log( 39 | `Moved ${filesToSend 40 | .map((f) => f.replace(".generated", "")) 41 | .join(", ")} to '${tscWD}/src/lib'.`, 42 | ); 43 | -------------------------------------------------------------------------------- /deploy/onUpdateFailure.js: -------------------------------------------------------------------------------- 1 | import { Octokit } from "@octokit/rest"; 2 | 3 | const authToken = process.env.GITHUB_TOKEN || process.env.GITHUB_API_TOKEN; 4 | const octokit = new Octokit({ auth: authToken }); 5 | await octokit.issues.createComment({ 6 | owner: "microsoft", 7 | repo: "TypeScript-DOM-lib-generator", 8 | issue_number: 1282, 9 | body: "Hello contributors in this thread, kindly pinging y'all as the 'Update core dependencies' job failed today. Please fix it when you have time, thanks!", 10 | }); 11 | -------------------------------------------------------------------------------- /deploy/readmes/audioworklet.md: -------------------------------------------------------------------------------- 1 | ### `@types/audioworklet` - Types for the global scope of Audio Worklets 2 | 3 | > The AudioWorklet interface of the Web Audio API is used to supply custom audio processing scripts that execute in a separate thread to provide very low latency audio processing. The worklet's code is run in the AudioWorkletGlobalScope global execution context, using a separate Web Audio thread which is shared by the worklet and other audio nodes. 4 | 5 | From [MDN Web Docs: AudioWorklet](https://developer.mozilla.org/en-US/docs/Web/API/AudioWorklet) 6 | 7 | This package contains type definitions which will set up the global environment for your TypeScript project to match the runtime environment of an Audio Worklet. The APIs inside `@types/audioworklet` are [generated from](https://github.com/microsoft/TypeScript-DOM-lib-generator/) the specifications for [Web Audio](https://webaudio.github.io/web-audio-api/). 8 | 9 | ## Installation 10 | 11 | To use `@types/audioworklet` you need to do two things: 12 | 13 | 1. Install the dependency: `npm install @types/audioworklet --save-dev`, `yarn add @types/audioworklet --dev` or `pnpm add @types/audioworklet --dev`. 14 | 1. Update your [`tsconfig.json`](https://www.typescriptlang.org/tsconfig) to avoid clashing with the DOM APIs. There are two cases to consider depending on if you have `lib` defined in your `tsconfig.json` or not. 15 | 16 | 1. **Without "lib"** - You will need to add `"lib": []`. The value you want to add inside your lib should correlate to your [`"target"`](https://www.typescriptlang.org/tsconfig#target). For example if you had `"target": "es2017"`, then you would add `"lib": ["es2017"]` 17 | 1. **With "lib"** - You should remove `"dom"`. 18 | 19 | That's all. 20 | 21 | If you'd like to ensure that the DOM types are never accidentally included, you can use [@orta/types-noop](https://www.npmjs.com/package/@orta/type-noops) in TypeScript 4.5+. 22 | 23 | ## SemVer 24 | 25 | This project does not respect semantic versioning as almost every change could potentially break a project, though we try to minimize removing types. 26 | 27 | `@types/audioworklet` follow the specifications, so when they mark a function/object/API/type as deprecated or removed - that is respected. 28 | 29 | ## Deploy Metadata 30 | 31 | You can read what changed in version {{version}} at {{release_href}}. -------------------------------------------------------------------------------- /deploy/readmes/serviceworker.md: -------------------------------------------------------------------------------- 1 | ### `@types/serviceworker` - Types for the global scope of Service Workers 2 | 3 | > Service workers essentially act as proxy servers that sit between web applications, the browser, and the network (when available). They are intended, among other things, to enable the creation of effective offline experiences, intercept network requests and take appropriate action based on whether the network is available, and update assets residing on the server. They will also allow access to push notifications and background sync APIs. 4 | 5 | From [MDN Web Docs: Service Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) 6 | 7 | This package contains type definitions which will set up the global environment for your TypeScript project to match the runtime environment of a Service Worker. The APIs inside `@types/serviceworker` are [generated from](https://github.com/microsoft/TypeScript-DOM-lib-generator/) the specifications for JavaScript. Given the size and state of constant change in web browsers, `@types/serviceworker` only has APIs which have passed a certain level of standardization and are available in at least two of the most popular browser engines. 8 | 9 | ## Installation 10 | 11 | To use `@types/serviceworker` you need to do two things: 12 | 13 | 1. Install the dependency: `npm install @types/serviceworker --save-dev`, `yarn add @types/serviceworker --dev` or `pnpm add @types/serviceworker --dev`. 14 | 1. Update your [`tsconfig.json`](https://www.typescriptlang.org/tsconfig) to avoid clashing with the DOM APIs. There are two cases to consider depending on if you have `lib` defined in your `tsconfig.json` or not. 15 | 16 | 1. **Without "lib"** - You will need to add `"lib": []`. The value you want to add inside your lib should correlate to your [`"target"`](https://www.typescriptlang.org/tsconfig#target). For example if you had `"target": "es2017"`, then you would add `"lib": ["es2017"]` 17 | 1. **With "lib"** - You should remove `"dom"`. 18 | 19 | That's all. 20 | 21 | If you'd like to ensure that the DOM types are never accidentally included, you can use [@orta/types-noop](https://www.npmjs.com/package/@orta/type-noops) in TypeScript 4.5+. 22 | 23 | ## SemVer 24 | 25 | This project does not respect semantic versioning as almost every change could potentially break a project, though we try to minimize removing types. 26 | 27 | `@types/serviceworker` follow the specifications, so when they mark a function/object/API/type as deprecated or removed - that is respected. 28 | 29 | ## Deploy Metadata 30 | 31 | You can read what changed in version {{version}} at {{release_href}}. -------------------------------------------------------------------------------- /deploy/readmes/sharedworker.md: -------------------------------------------------------------------------------- 1 | ### `@types/sharedworker` - Types for the global scope of Web Workers 2 | 3 | > The SharedWorker interface represents a specific kind of worker that can be accessed from several browsing contexts, such as several windows, iframes or even workers. They implement an interface different than dedicated workers and have a different global scope, `SharedWorkerGlobalScope`. 4 | 5 | From [MDN Web Docs: SharedWorker API](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) 6 | 7 | This package contains type definitions which will set up the global environment for your TypeScript project to match the runtime environment of a Web Worker. The APIs inside `@types/sharedworker` are [generated from](https://github.com/microsoft/TypeScript-DOM-lib-generator/) the specifications for JavaScript. 8 | 9 | ## Installation 10 | 11 | To use `@types/sharedworker` you need to do two things: 12 | 13 | 1. Install the dependency: `npm install @types/sharedworker --save-dev`, `yarn add @types/sharedworker --dev` or `pnpm add @types/sharedworker --dev`. 14 | 15 | 1. Update your [`tsconfig.json`](https://www.typescriptlang.org/tsconfig) to avoid clashing with the DOM APIs. There are two cases to consider depending on if you have `lib` defined in your `tsconfig.json` or not. 16 | 17 | 1. **Without "lib"** - You will need to add `"lib": []`. The value you want to add inside your lib should correlate to your [`"target"`](https://www.typescriptlang.org/tsconfig#target). For example if you had `"target": "es2017"`, then you would add `"lib": ["es2017"]` 18 | 1. **With "lib"** - You should remove `"dom"`. 19 | 20 | 21 | If you'd like to ensure that the DOM types are never accidentally included, you can use [@orta/types-noop](https://www.npmjs.com/package/@orta/type-noops) in TypeScript 4.5+. 22 | 23 | ## SemVer 24 | 25 | This project does not respect semantic versioning as almost every change could potentially break a project, though we try to minimize removing types. 26 | 27 | `@types/sharedworker` follow the specifications, so when they mark a function/object/API/type as deprecated or removed - that is respected. 28 | 29 | ## Deploy Metadata 30 | 31 | You can read what changed in version {{version}} at {{release_href}}. -------------------------------------------------------------------------------- /deploy/readmes/web.md: -------------------------------------------------------------------------------- 1 | ### `@types/web` - Types for the DOM and most web-related APIs 2 | 3 | This module contains the DOM types for the majority of the web APIs used in a web browser. 4 | 5 | The APIs inside `@types/web` are [generated from](https://github.com/microsoft/TypeScript-DOM-lib-generator/) the specifications for CSS, HTML and JavaScript. Given the size and state of constant change in web browsers, `@types/web` only has APIs which have passed a certain level of standardization and are available in at least two of the most popular browser engines. 6 | 7 | `@types/web` is also included inside TypeScript, available as `dom` in the [`lib`](https://www.typescriptlang.org/tsconfig#lib) section and included in projects by default. By using `@types/web` you can lock your the web APIs used in your projects, easing the process of updating TypeScript and offering more control in your environment. 8 | 9 | ## Installation 10 | 11 | With TypeScript 4.5+ using [lib replacement](https://www.typescriptlang.org/tsconfig/#libReplacement), you can swap the DOM lib with this dependency: 12 | 13 | ```sh 14 | pnpm add @typescript/lib-dom@npm:@types/web --save-dev 15 | npm install @typescript/lib-dom@npm:@types/web --save-dev 16 | yarn add @typescript/lib-dom@npm:@types/web --dev 17 | ``` 18 | 19 | That's all. 20 | 21 |
22 | TypeScript 4.4 and below 23 | 24 |
25 | To use `@types/web` you need to do two things: 26 | 27 | 1. Install the dependency: `npm install @types/web --save-dev`, `yarn add @types/web --dev` or `pnpm add @types/web --dev`. 28 | 29 | 1. Update your [`tsconfig.json`](https://www.typescriptlang.org/tsconfig). There are two cases to consider depending on if you have `lib` defined in your `tsconfig.json` or not. 30 | 31 | 1. **Without "lib"** - You will need to add `"lib": []`. The value you want to add inside your lib should correlate to your [`"target"`](https://www.typescriptlang.org/tsconfig#target). For example if you had `"target": "es2017"`, then you would add `"lib": ["es2017"]` 32 | 1. **With "lib"** - You should remove `"dom"`. 33 | 34 | Removing `"dom"` gives @types/web the chance to provide the same set of global declarations. However, It's possible that your dependencies pull in the TypeScript DOM library, in which case you can either try to make that not happen, or use TypeScript 4.5 to systematically replace the library. 35 | 36 |
37 | 38 | 39 | ## SemVer 40 | 41 | This project does not respect semantic versioning as almost every change could potentially break a project, though we try to minimize removing types. 42 | `@types/web` follow the specifications, so when they mark a function/object/API/type as deprecated or removed - that is respected. 43 | 44 | ## TypeScript Version Support 45 | 46 | Prior to `@types/web` the web APIs were deployed with a version of TypeScript, and backwards compatibility has not been a concern. Now the web APIs and TypeScript can be de-coupled, then we expect to eventually hit a point where we take backwards compatibility in mind. For now, `@types/web` officially supports TypeScript 4.4 and above. It very likely will work with TypeScript versions much earlier that that however. 47 | 48 | ## Deploy Metadata 49 | 50 | You can read what changed in version {{version}} at {{release_href}}. 51 | -------------------------------------------------------------------------------- /deploy/readmes/webworker.md: -------------------------------------------------------------------------------- 1 | ### `@types/webworker` - Types for the global scope of Web Workers 2 | 3 | > The Worker interface of the Web Workers API represents a background task that can be created via script, which can send messages back to its creator. Creating a worker is done by calling the `Worker("path/to/worker/script")` constructor. 4 | 5 | From [MDN Web Docs: Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Worker) 6 | 7 | This package contains type definitions which will set up the global environment for your TypeScript project to match the runtime environment of a Web Worker. The APIs inside `@types/webworker` are [generated from](https://github.com/microsoft/TypeScript-DOM-lib-generator/) the specifications for JavaScript. 8 | 9 | ## Installation 10 | 11 | With TypeScript 4.5+ using [lib replacement](https://www.typescriptlang.org/tsconfig/#libReplacement), you can swap the WebWorker lib with this dependency: 12 | 13 | ```sh 14 | pnpm add @typescript/lib-webworker@npm:@types/webworker --save-dev 15 | npm install @typescript/lib-webworker@npm:@types/webworker --save-dev 16 | yarn add @typescript/lib-webworker@npm:@types/webworker --dev 17 | ``` 18 | 19 | That's all. 20 | 21 |
22 | TypeScript 4.4 and below 23 | 24 |
25 | To use `@types/webworker` you need to do two things: 26 | 27 | 1. Install the dependency: `npm install @types/webworker --save-dev`, `yarn add @types/webworker --dev` or `pnpm add @types/webworker --save-dev`. 28 | 29 | 1. Update your [`tsconfig.json`](https://www.typescriptlang.org/tsconfig). There are two cases to consider depending on if you have `lib` defined in your `tsconfig.json` or not. 30 | 31 | 1. **Without "lib"** - You will need to add `"lib": []`. The value you want to add inside your lib should correlate to your [`"target"`](https://www.typescriptlang.org/tsconfig#target). For example if you had `"target": "es2017"`, then you would add `"lib": ["es2017"]` 32 | 1. **With "lib"** - You should remove `"webworker"`. 33 | 34 | Removing `"webworker"` gives @types/webworker the chance to provide the same set of global declarations. However, It's possible that your dependencies pull in the TypeScript Web Worker library, in which case you can either try to make that not happen, or use TypeScript 4.5 to systematically replace the library. 35 | 36 |
37 | 38 | If you'd like to ensure that the DOM types are never accidentally included, you can use [@orta/types-noop](https://www.npmjs.com/package/@orta/type-noops) in TypeScript 4.5+. 39 | 40 | ## SemVer 41 | 42 | This project does not respect semantic versioning as almost every change could potentially break a project, though we try to minimize removing types. 43 | 44 | `@types/webworker` follow the specifications, so when they mark a function/object/API/type as deprecated or removed - that is respected. 45 | 46 | ## Deploy Metadata 47 | 48 | You can read what changed in version {{version}} at {{release_href}}. 49 | -------------------------------------------------------------------------------- /deploy/template/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 16 | 17 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 18 | 19 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 20 | 21 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 22 | 23 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 24 | 25 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 26 | 27 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 28 | 29 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 30 | 31 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 32 | 33 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 34 | 35 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 36 | 37 | You must give any other recipients of the Work or Derivative Works a copy of this License; and 38 | 39 | You must cause any modified files to carry prominent notices stating that You changed the files; and 40 | 41 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 42 | 43 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 44 | 45 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 46 | 47 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 48 | 49 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 50 | 51 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 52 | 53 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 54 | 55 | END OF TERMS AND CONDITIONS 56 | -------------------------------------------------------------------------------- /deploy/template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@types/xyz", 3 | "version": "0.0.x", 4 | "description": "TypeScript definitions for xyz", 5 | "license": "Apache-2.0", 6 | "contributors": [], 7 | "main": "", 8 | "types": "index.d.ts", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/microsoft/TypeScript-DOM-Lib-Generator.git" 12 | }, 13 | "typesVersions": { 14 | "<=5.5": { "*": ["ts5.5/*"] }, 15 | "<=5.6": { "*": ["ts5.6/*"] } 16 | }, 17 | "scripts": {}, 18 | "dependencies": {} 19 | } -------------------------------------------------------------------------------- /deploy/versionChangelog.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | // npm run ts-changelog @types/web 0.0.1 0.0.3 4 | 5 | import { gitShowFile, generateChangelogFrom } from "../lib/changelog.js"; 6 | import { packages } from "./createTypesPackages.js"; 7 | import { basename } from "path"; 8 | 9 | const [name, before, to] = process.argv.slice(2); 10 | if (!name || !before || !to) { 11 | throw new Error( 12 | "Expected three arguments: package name, version before, version to", 13 | ); 14 | } 15 | 16 | const go = () => { 17 | // We'll need to map back from the filename in the npm package to the 18 | // generated file in baselines inside the git tag 19 | const thisPackageMeta = packages.find((p) => p.name === name); 20 | if (!thisPackageMeta) 21 | throw new Error(`Could not find ${name} in ${packages.map((p) => p.name)}`); 22 | 23 | for (const file of thisPackageMeta.files) { 24 | const filename = `baselines/${basename(file.from)}`; 25 | const beforeFileText = gitShowFile(`${name}@${before}`, filename); 26 | const toFileText = gitShowFile(`${name}@${to}`, filename); 27 | 28 | const notes = generateChangelogFrom(beforeFileText, toFileText); 29 | 30 | console.log(`\n## \`${file.to}\`\n`); 31 | console.log(notes.trim() === "" ? "No changes" : notes); 32 | } 33 | }; 34 | 35 | go(); 36 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | import typescriptEslint from "typescript-eslint"; 4 | import globals from "globals"; 5 | import js from "@eslint/js"; 6 | import prettierRecommended from "eslint-plugin-prettier/recommended"; 7 | 8 | export default typescriptEslint.config( 9 | js.configs.recommended, 10 | ...typescriptEslint.configs.recommended, 11 | prettierRecommended, 12 | { 13 | languageOptions: { 14 | globals: { 15 | ...globals.node, 16 | ...globals.browser, 17 | }, 18 | }, 19 | 20 | rules: { 21 | "no-prototype-builtins": 0, 22 | "@typescript-eslint/no-explicit-any": 0, 23 | "@typescript-eslint/no-non-null-assertion": 0, 24 | "@typescript-eslint/no-var-requires": 0, 25 | }, 26 | }, 27 | ); 28 | -------------------------------------------------------------------------------- /inputfiles/README.md: -------------------------------------------------------------------------------- 1 | # Hey! Read This! 2 | 3 | Some files in this directory are generated. Please do not edit them. 4 | This specifically includes: 5 | 6 | * `idl/*` 7 | * `mdn/*` 8 | 9 | Feel free to send a pull request with changes to any other files. 10 | -------------------------------------------------------------------------------- /inputfiles/deprecatedMessage.json: -------------------------------------------------------------------------------- 1 | { 2 | "MutationEvent": "DOM4 [DOM] provides a new mechanism using a MutationObserver interface which addresses the use cases that mutation events solve, but in a more performant manner. Thus, this specification describes mutation events for reference and completeness of legacy behavior, but deprecates the use of the MutationEvent interface.", 3 | "PerformanceTiming": "This interface is deprecated in the Navigation Timing Level 2 specification. Please use the PerformanceNavigationTiming interface instead.", 4 | "PerformanceNavigation": "This interface is deprecated in the Navigation Timing Level 2 specification. Please use the PerformanceNavigationTiming interface instead.", 5 | "AudioProcessingEvent": "As of the August 29 2014 Web Audio API spec publication, this feature has been marked as deprecated, and is soon to be replaced by AudioWorklet.", 6 | "ScriptProcessorNode": "As of the August 29 2014 Web Audio API spec publication, this feature has been marked as deprecated, and was replaced by AudioWorklet (see AudioWorkletNode)." 7 | } 8 | -------------------------------------------------------------------------------- /inputfiles/idl/IndexedDB.commentmap.json: -------------------------------------------------------------------------------- 1 | { 2 | "idbrequest-result": "When a request is completed, returns the result, or undefined if the request failed. Throws a \"InvalidStateError\" DOMException if the request is still pending.", 3 | "idbrequest-error": "When a request is completed, returns the error (a DOMException), or null if the request succeeded. Throws a \"InvalidStateError\" DOMException if the request is still pending.", 4 | "idbrequest-source": "Returns the IDBObjectStore, IDBIndex, or IDBCursor the request was made against, or null if is was an open request.", 5 | "idbrequest-transaction": "Returns the IDBTransaction the request was made within. If this as an open request, then it returns an upgrade transaction while it is running, or null otherwise.", 6 | "idbrequest-readystate": "Returns \"pending\" until a request is complete, then returns \"done\".", 7 | "idbfactory-open": "Attempts to open a connection to the named database with the current version, or 1 if it does not already exist. If the request is successful request's result will be the connection.", 8 | "idbfactory-deletedatabase": "Attempts to delete the named database. If the database already exists and there are open connections that don't close in response to a versionchange event, the request will be blocked until all they close. If the request is successful request's result will be null.", 9 | "idbfactory-cmp": "Compares two values as keys. Returns -1 if key1 precedes key2, 1 if key2 precedes key1, and 0 if the keys are equal.\n\nThrows a \"DataError\" DOMException if either input is not a valid key.", 10 | "idbdatabase-name": "Returns the name of the database.", 11 | "idbdatabase-version": "Returns the version of the database.", 12 | "idbdatabase-objectstorenames": "Returns a list of the names of object stores in the database.", 13 | "idbdatabase-createobjectstore": "Creates a new object store with the given name and options and returns a new IDBObjectStore.\n\nThrows a \"InvalidStateError\" DOMException if not called within an upgrade transaction.", 14 | "idbdatabase-deleteobjectstore": "Deletes the object store with the given name.\n\nThrows a \"InvalidStateError\" DOMException if not called within an upgrade transaction.", 15 | "idbdatabase-transaction": "Returns a new transaction with the given mode (\"readonly\" or \"readwrite\") and scope which can be a single object store name or an array of names.", 16 | "idbdatabase-close": "Closes the connection once all running transactions have finished.", 17 | "idbobjectstore-name": "Returns the name of the store.", 18 | "idbobjectstore-keypath": "Returns the key path of the store, or null if none.", 19 | "idbobjectstore-indexnames": "Returns a list of the names of indexes in the store.", 20 | "idbobjectstore-transaction": "Returns the associated transaction.", 21 | "idbobjectstore-autoincrement": "Returns true if the store has a key generator, and false otherwise.", 22 | "idbobjectstore-put": "Adds or updates a record in store with the given value and key.\n\nIf the store uses in-line keys and key is specified a \"DataError\" DOMException will be thrown.\n\nIf put() is used, any existing record with the key will be replaced. If add() is used, and if a record with the key already exists the request will fail, with request's error set to a \"ConstraintError\" DOMException.\n\nIf successful, request's result will be the record's key.", 23 | "idbobjectstore-add": "Adds or updates a record in store with the given value and key.\n\nIf the store uses in-line keys and key is specified a \"DataError\" DOMException will be thrown.\n\nIf put() is used, any existing record with the key will be replaced. If add() is used, and if a record with the key already exists the request will fail, with request's error set to a \"ConstraintError\" DOMException.\n\nIf successful, request's result will be the record's key.", 24 | "idbobjectstore-delete": "Deletes records in store with the given key or in the given key range in query.\n\nIf successful, request's result will be undefined.", 25 | "idbobjectstore-clear": "Deletes all records in store.\n\nIf successful, request's result will be undefined.", 26 | "idbobjectstore-get": "Retrieves the value of the first record matching the given key or key range in query.\n\nIf successful, request's result will be the value, or undefined if there was no matching record.", 27 | "idbobjectstore-getkey": "Retrieves the key of the first record matching the given key or key range in query.\n\nIf successful, request's result will be the key, or undefined if there was no matching record.", 28 | "idbobjectstore-getall": "Retrieves the values of the records matching the given key or key range in query (up to count if given).\n\nIf successful, request's result will be an Array of the values.", 29 | "idbobjectstore-getallkeys": "Retrieves the keys of records matching the given key or key range in query (up to count if given).\n\nIf successful, request's result will be an Array of the keys.", 30 | "idbobjectstore-count": "Retrieves the number of records matching the given key or key range in query.\n\nIf successful, request's result will be the count.", 31 | "idbobjectstore-opencursor": "Opens a cursor over the records matching query, ordered by direction. If query is null, all records in store are matched.\n\nIf successful, request's result will be an IDBCursorWithValue pointing at the first matching record, or null if there were no matching records.", 32 | "idbobjectstore-openkeycursor": "Opens a cursor with key only flag set over the records matching query, ordered by direction. If query is null, all records in store are matched.\n\nIf successful, request's result will be an IDBCursor pointing at the first matching record, or null if there were no matching records.", 33 | "idbobjectstore-createindex": "Creates a new index in store with the given name, keyPath and options and returns a new IDBIndex. If the keyPath and options define constraints that cannot be satisfied with the data already in store the upgrade transaction will abort with a \"ConstraintError\" DOMException.\n\nThrows an \"InvalidStateError\" DOMException if not called within an upgrade transaction.", 34 | "idbobjectstore-deleteindex": "Deletes the index in store with the given name.\n\nThrows an \"InvalidStateError\" DOMException if not called within an upgrade transaction.", 35 | "idbindex-name": "Returns the name of the index.", 36 | "idbindex-objectstore": "Returns the IDBObjectStore the index belongs to.", 37 | "idbindex-get": "Retrieves the value of the first record matching the given key or key range in query.\n\nIf successful, request's result will be the value, or undefined if there was no matching record.", 38 | "idbindex-getkey": "Retrieves the key of the first record matching the given key or key range in query.\n\nIf successful, request's result will be the key, or undefined if there was no matching record.", 39 | "idbindex-getall": "Retrieves the values of the records matching the given key or key range in query (up to count if given).\n\nIf successful, request's result will be an Array of the values.", 40 | "idbindex-getallkeys": "Retrieves the keys of records matching the given key or key range in query (up to count if given).\n\nIf successful, request's result will be an Array of the keys.", 41 | "idbindex-count": "Retrieves the number of records matching the given key or key range in query.\n\nIf successful, request's result will be the count.", 42 | "idbindex-opencursor": "Opens a cursor over the records matching query, ordered by direction. If query is null, all records in index are matched.\n\nIf successful, request's result will be an IDBCursorWithValue, or null if there were no matching records.", 43 | "idbindex-openkeycursor": "Opens a cursor with key only flag set over the records matching query, ordered by direction. If query is null, all records in index are matched.\n\nIf successful, request's result will be an IDBCursor, or null if there were no matching records.", 44 | "idbkeyrange-lower": "Returns lower bound, or undefined if none.", 45 | "idbkeyrange-upper": "Returns upper bound, or undefined if none.", 46 | "idbkeyrange-loweropen": "Returns true if the lower open flag is set, and false otherwise.", 47 | "idbkeyrange-upperopen": "Returns true if the upper open flag is set, and false otherwise.", 48 | "idbkeyrange-only": "Returns a new IDBKeyRange spanning only key.", 49 | "idbkeyrange-lowerbound": "Returns a new IDBKeyRange starting at key with no upper bound. If open is true, key is not included in the range.", 50 | "idbkeyrange-upperbound": "Returns a new IDBKeyRange with no lower bound and ending at key. If open is true, key is not included in the range.", 51 | "idbkeyrange-bound": "Returns a new IDBKeyRange spanning from lower to upper. If lowerOpen is true, lower is not included in the range. If upperOpen is true, upper is not included in the range.", 52 | "idbkeyrange-includes": "Returns true if key is included in the range, and false otherwise.", 53 | "idbcursor-source": "Returns the IDBObjectStore or IDBIndex the cursor was opened from.", 54 | "idbcursor-direction": "Returns the direction (\"next\", \"nextunique\", \"prev\" or \"prevunique\") of the cursor.", 55 | "idbcursor-key": "Returns the key of the cursor. Throws a \"InvalidStateError\" DOMException if the cursor is advancing or is finished.", 56 | "idbcursor-primarykey": "Returns the effective key of the cursor. Throws a \"InvalidStateError\" DOMException if the cursor is advancing or is finished.", 57 | "idbcursor-advance": "Advances the cursor through the next count records in range.", 58 | "idbcursor-continue": "Advances the cursor to the next record in range.", 59 | "idbcursor-continueprimarykey": "Advances the cursor to the next record in range matching or after key and primaryKey. Throws an \"InvalidAccessError\" DOMException if the source is not an index.", 60 | "idbcursor-update": "Updated the record pointed at by the cursor with a new value.\n\nThrows a \"DataError\" DOMException if the effective object store uses in-line keys and the key would have changed.\n\nIf successful, request's result will be the record's key.", 61 | "idbcursor-delete": "Delete the record pointed at by the cursor with a new value.\n\nIf successful, request's result will be undefined.", 62 | "idbcursorwithvalue-value": "Returns the cursor's current value.", 63 | "idbtransaction-objectstorenames": "Returns a list of the names of object stores in the transaction's scope. For an upgrade transaction this is all object stores in the database.", 64 | "idbtransaction-mode": "Returns the mode the transaction was created with (\"readonly\" or \"readwrite\"), or \"versionchange\" for an upgrade transaction.", 65 | "idbtransaction-db": "Returns the transaction's connection.", 66 | "idbtransaction-error": "If the transaction was aborted, returns the error (a DOMException) providing the reason.", 67 | "idbtransaction-objectstore": "Returns an IDBObjectStore in the transaction's scope.", 68 | "idbtransaction-abort": "Aborts the transaction. All pending requests will fail with a \"AbortError\" DOMException and all changes made to the database will be reverted." 69 | } 70 | -------------------------------------------------------------------------------- /inputfiles/idl/encoding.commentmap.json: -------------------------------------------------------------------------------- 1 | { 2 | "textdecoder": "Returns a new TextDecoder object.\n\nIf label is either not a label or is a label for replacement, throws a RangeError.", 3 | "textdecodercommon-encoding": "Returns encoding's name, lowercased.", 4 | "textdecodercommon-fatal": "Returns true if error mode is \"fatal\", otherwise false.", 5 | "textdecodercommon-ignorebom": "Returns the value of ignore BOM.", 6 | "textdecoder-decode": "Returns the result of running encoding's decoder. The method can be invoked zero or more times with options's stream set to true, and then once without options's stream (or set to false), to process a fragmented input. If the invocation without options's stream (or set to false) has no input, it's clearest to omit both arguments.\n\n```\nvar string = \"\", decoder = new TextDecoder(encoding), buffer;\nwhile(buffer = next_chunk()) {\n string += decoder.decode(buffer, {stream:true});\n}\nstring += decoder.decode(); // end-of-queue\n```\n\nIf the error mode is \"fatal\" and encoding's decoder returns error, throws a TypeError.", 7 | "textencoder": "Returns a new TextEncoder object.", 8 | "textencodercommon-encoding": "Returns \"utf-8\".", 9 | "textencoder-encode": "Returns the result of running UTF-8's encoder.", 10 | "textencoder-encodeinto": "Runs the UTF-8 encoder on source, stores the result of that operation into destination, and returns the progress made as an object wherein read is the number of converted code units of source and written is the number of bytes modified in destination.", 11 | "textdecoderstream": "Returns a new TextDecoderStream object.\n\nIf label is either not a label or is a label for replacement, throws a RangeError.", 12 | "generictransformstream-readable": "Returns a readable stream whose chunks are strings resulting from running encoding's decoder on the chunks written to writable.", 13 | "generictransformstream-writable": "Returns a writable stream which accepts [AllowShared] BufferSource chunks and runs them through encoding's decoder before making them available to readable.\n\nTypically this will be used via the pipeThrough() method on a ReadableStream source.\n\n```\nvar decoder = new TextDecoderStream(encoding);\nbyteReadable\n .pipeThrough(decoder)\n .pipeTo(textWritable);\n```\n\nIf the error mode is \"fatal\" and encoding's decoder returns error, both readable and writable will be errored with a TypeError.", 14 | "textencoderstream": "Returns a new TextEncoderStream object." 15 | } 16 | -------------------------------------------------------------------------------- /inputfiles/idl/fetch.commentmap.json: -------------------------------------------------------------------------------- 1 | { 2 | "request": "Returns a new request whose url property is input if input is a string, and input's url if input is a Request object.\n\nThe init argument is an object whose properties can be set as follows:", 3 | "request-method": "Returns request's HTTP method, which is \"GET\" by default.", 4 | "request-url": "Returns the URL of request as a string.", 5 | "request-headers": "Returns a Headers object consisting of the headers associated with request. Note that headers added in the network layer by the user agent will not be accounted for in this object, e.g., the \"Host\" header.", 6 | "request-destination": "Returns the kind of resource requested by request, e.g., \"document\" or \"script\".", 7 | "request-referrer": "Returns the referrer of request. Its value can be a same-origin URL if explicitly set in init, the empty string to indicate no referrer, and \"about:client\" when defaulting to the global's default. This is used during fetching to determine the value of the `Referer` header of the request being made.", 8 | "request-referrerpolicy": "Returns the referrer policy associated with request. This is used during fetching to compute the value of the request's referrer.", 9 | "request-mode": "Returns the mode associated with request, which is a string indicating whether the request will use CORS, or will be restricted to same-origin URLs.", 10 | "request-credentials": "Returns the credentials mode associated with request, which is a string indicating whether credentials will be sent with the request always, never, or only when sent to a same-origin URL.", 11 | "request-cache": "Returns the cache mode associated with request, which is a string indicating how the request will interact with the browser's cache when fetching.", 12 | "request-redirect": "Returns the redirect mode associated with request, which is a string indicating how redirects for the request will be handled during fetching. A request will follow redirects by default.", 13 | "request-integrity": "Returns request's subresource integrity metadata, which is a cryptographic hash of the resource being fetched. Its value consists of multiple hashes separated by whitespace. [SRI]", 14 | "request-keepalive": "Returns a boolean indicating whether or not request can outlive the global in which it was created.", 15 | "request-isreloadnavigation": "Returns a boolean indicating whether or not request is for a reload navigation.", 16 | "request-ishistorynavigation": "Returns a boolean indicating whether or not request is for a history navigation (a.k.a. back-foward navigation).", 17 | "request-signal": "Returns the signal associated with request, which is an AbortSignal object indicating whether or not request has been aborted, and its abort event handler.", 18 | "requestinit-method": "A string to set request's method.", 19 | "requestinit-headers": "A Headers object, an object literal, or an array of two-item arrays to set request's headers.", 20 | "requestinit-body": "A BodyInit object or null to set request's body.", 21 | "requestinit-referrer": "A string whose value is a same-origin URL, \"about:client\", or the empty string, to set request's referrer.", 22 | "requestinit-referrerpolicy": "A referrer policy to set request's referrerPolicy.", 23 | "requestinit-mode": "A string to indicate whether the request will use CORS, or will be restricted to same-origin URLs. Sets request's mode.", 24 | "requestinit-credentials": "A string indicating whether credentials will be sent with the request always, never, or only when sent to a same-origin URL. Sets request's credentials.", 25 | "requestinit-cache": "A string indicating how the request will interact with the browser's cache to set request's cache.", 26 | "requestinit-redirect": "A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect.", 27 | "requestinit-integrity": "A cryptographic hash of the resource to be fetched by request. Sets request's integrity.", 28 | "requestinit-keepalive": "A boolean to set request's keepalive.", 29 | "requestinit-signal": "An AbortSignal to set request's signal.", 30 | "requestinit-window": "Can only be null. Used to disassociate request from any Window." 31 | } 32 | -------------------------------------------------------------------------------- /inputfiles/idl/fullscreen.commentmap.json: -------------------------------------------------------------------------------- 1 | { 2 | "element-requestfullscreen": "Displays element fullscreen and resolves promise when done.\n\nWhen supplied, options's navigationUI member indicates whether showing navigation UI while in fullscreen is preferred or not. If set to \"show\", navigation simplicity is preferred over screen space, and if set to \"hide\", more screen space is preferred. User agents are always free to honor user preference over the application's. The default value \"auto\" indicates no application preference.", 3 | "document-fullscreenenabled": "Returns true if document has the ability to display elements fullscreen and fullscreen is supported, or false otherwise.", 4 | "document-exitfullscreen": "Stops document's fullscreen element from being displayed fullscreen and resolves promise when done.", 5 | "documentorshadowroot-fullscreenelement": "Returns document's fullscreen element." 6 | } 7 | -------------------------------------------------------------------------------- /inputfiles/idl/streams.commentmap.json: -------------------------------------------------------------------------------- 1 | { 2 | "readablestreamgetreaderoptions-mode": "Creates a ReadableStreamBYOBReader and locks the stream to the new reader.\n\nThis call behaves the same way as the no-argument variant, except that it only works on readable byte streams, i.e. streams which were constructed specifically with the ability to handle \"bring your own buffer\" reading. The returned BYOB reader provides the ability to directly read individual chunks from the stream via its read() method, into developer-supplied buffers, allowing more precise control over allocation.", 3 | "readablewritablepair-writable": "Provides a convenient, chainable way of piping this readable stream through a transform stream (or any other { writable, readable } pair). It simply pipes the stream into the writable side of the supplied pair, and returns the readable side for further use.\n\nPiping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader.", 4 | "streampipeoptions-preventclose": "Pipes this readable stream to a given writable stream destination. The way in which the piping process behaves under various error conditions can be customized with a number of passed options. It returns a promise that fulfills when the piping process completes successfully, or rejects if any errors were encountered.\n\nPiping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader.\n\nErrors and closures of the source and destination streams propagate as follows:\n\nAn error in this source readable stream will abort destination, unless preventAbort is truthy. The returned promise will be rejected with the source's error, or with any error that occurs during aborting the destination.\n\nAn error in destination will cancel this source readable stream, unless preventCancel is truthy. The returned promise will be rejected with the destination's error, or with any error that occurs during canceling the source.\n\nWhen this source readable stream closes, destination will be closed, unless preventClose is truthy. The returned promise will be fulfilled once this process completes, unless an error is encountered while closing the destination, in which case it will be rejected with that error.\n\nIf destination starts out closed or closing, this source readable stream will be canceled, unless preventCancel is true. The returned promise will be rejected with an error indicating piping to a closed stream failed, or with any error that occurs during canceling the source.\n\nThe signal option can be set to an AbortSignal to allow aborting an ongoing pipe operation via the corresponding AbortController. In this case, this source readable stream will be canceled, and destination aborted, unless the respective options preventCancel or preventAbort are set.", 5 | "readablestreamiteratoroptions-preventcancel": "Asynchronously iterates over the chunks in the stream's internal queue.\n\nAsynchronously iterating over the stream will lock it, preventing any other consumer from acquiring a reader. The lock will be released if the async iterator's return() method is called, e.g. by breaking out of the loop.\n\nBy default, calling the async iterator's return() method will also cancel the stream. To prevent this, use the stream's values() method, passing true for the preventCancel option.", 6 | "queuingstrategyinit-highwatermark": "Creates a new ByteLengthQueuingStrategy with the provided high water mark.\n\nNote that the provided high water mark will not be validated ahead of time. Instead, if it is negative, NaN, or not a number, the resulting ByteLengthQueuingStrategy will cause the corresponding stream constructor to throw." 7 | } 8 | -------------------------------------------------------------------------------- /inputfiles/idl/websockets.commentmap.json: -------------------------------------------------------------------------------- 1 | { 2 | "closeevent-wasclean": "Returns true if the connection closed cleanly; false otherwise.", 3 | "closeevent-code": "Returns the WebSocket connection close code provided by the server.", 4 | "closeevent-reason": "Returns the WebSocket connection close reason provided by the server.", 5 | "websocket": "Creates a new WebSocket object, immediately establishing the associated WebSocket connection.\n\nurl is a string giving the URL over which the connection is established. Only \"ws\" or \"wss\" schemes are allowed; others will cause a \"SyntaxError\" DOMException. URLs with fragments will also cause such an exception.\n\nprotocols is either a string or an array of strings. If it is a string, it is equivalent to an array consisting of just that string; if it is omitted, it is equivalent to the empty array. Each string in the array is a subprotocol name. The connection will only be established if the server reports that it has selected one of these subprotocols. The subprotocol names have to match the requirements for elements that comprise the value of Sec-WebSocket-Protocol fields as defined by The WebSocket protocol. [WSP]", 6 | "websocket-send": "Transmits data using the WebSocket connection. data can be a string, a Blob, an ArrayBuffer, or an ArrayBufferView.", 7 | "websocket-close": "Closes the WebSocket connection, optionally using code as the the WebSocket connection close code and reason as the the WebSocket connection close reason.", 8 | "websocket-url": "Returns the URL that was used to establish the WebSocket connection.", 9 | "websocket-readystate": "Returns the state of the WebSocket object's connection. It can have the values described below.", 10 | "websocket-bufferedamount": "Returns the number of bytes of application data (UTF-8 text and binary data) that have been queued using send() but not yet been transmitted to the network.\n\nIf the WebSocket connection is closed, this attribute's value will only increase with each call to the send() method. (The number does not reset to zero once the connection closes.)", 11 | "websocket-extensions": "Returns the extensions selected by the server, if any.", 12 | "websocket-protocol": "Returns the subprotocol selected by the server, if any. It can be used in conjunction with the array form of the constructor's second argument to perform subprotocol negotiation.", 13 | "websocket-binarytype": "Returns a string that indicates how binary data from the WebSocket object is exposed to scripts:\n\nCan be set, to change how binary data is returned. The default is \"blob\"." 14 | } 15 | -------------------------------------------------------------------------------- /inputfiles/idl/xhr.commentmap.json: -------------------------------------------------------------------------------- 1 | { 2 | "xmlhttprequest": "Returns a new XMLHttpRequest object.", 3 | "xmlhttprequest-readystate": "Returns client's state.", 4 | "xmlhttprequest-open": "Sets the request method, request URL, and synchronous flag.\n\nThrows a \"SyntaxError\" DOMException if either method is not a valid method or url cannot be parsed.\n\nThrows a \"SecurityError\" DOMException if method is a case-insensitive match for `CONNECT`, `TRACE`, or `TRACK`.\n\nThrows an \"InvalidAccessError\" DOMException if async is false, current global object is a Window object, and the timeout attribute is not zero or the responseType attribute is not the empty string.", 5 | "xmlhttprequest-setrequestheader": "Combines a header in author request headers.\n\nThrows an \"InvalidStateError\" DOMException if either state is not opened or the send() flag is set.\n\nThrows a \"SyntaxError\" DOMException if name is not a header name or if value is not a header value.", 6 | "xmlhttprequest-timeout": "Can be set to a time in milliseconds. When set to a non-zero value will cause fetching to terminate after the given time has passed. When the time has passed, the request has not yet completed, and this's synchronous flag is unset, a timeout event will then be dispatched, or a \"TimeoutError\" DOMException will be thrown otherwise (for the send() method).\n\nWhen set: throws an \"InvalidAccessError\" DOMException if the synchronous flag is set and current global object is a Window object.", 7 | "xmlhttprequest-withcredentials": "True when credentials are to be included in a cross-origin request. False when they are to be excluded in a cross-origin request and when cookies are to be ignored in its response. Initially false.\n\nWhen set: throws an \"InvalidStateError\" DOMException if state is not unsent or opened, or if the send() flag is set.", 8 | "xmlhttprequest-upload": "Returns the associated XMLHttpRequestUpload object. It can be used to gather transmission information when data is transferred to a server.", 9 | "xmlhttprequest-send": "Initiates the request. The body argument provides the request body, if any, and is ignored if the request method is GET or HEAD.\n\nThrows an \"InvalidStateError\" DOMException if either state is not opened or the send() flag is set.", 10 | "xmlhttprequest-abort": "Cancels any network activity.", 11 | "xmlhttprequest-overridemimetype": "Acts as if the `Content-Type` header value for a response is mime. (It does not change the header.)\n\nThrows an \"InvalidStateError\" DOMException if state is loading or done.", 12 | "xmlhttprequest-responsetype": "Returns the response type.\n\nCan be set to change the response type. Values are: the empty string (default), \"arraybuffer\", \"blob\", \"document\", \"json\", and \"text\".\n\nWhen set: setting to \"document\" is ignored if current global object is not a Window object.\n\nWhen set: throws an \"InvalidStateError\" DOMException if state is loading or done.\n\nWhen set: throws an \"InvalidAccessError\" DOMException if the synchronous flag is set and current global object is a Window object.", 13 | "xmlhttprequest-response": "Returns the response body.", 14 | "xmlhttprequest-responsetext": "Returns response as text.\n\nThrows an \"InvalidStateError\" DOMException if responseType is not the empty string or \"text\".", 15 | "xmlhttprequest-responsexml": "Returns the response as document.\n\nThrows an \"InvalidStateError\" DOMException if responseType is not the empty string or \"document\"." 16 | } 17 | -------------------------------------------------------------------------------- /inputfiles/knownTypes.json: -------------------------------------------------------------------------------- 1 | { 2 | "Window": [ 3 | "AesCbcParams", 4 | "AesCtrParams", 5 | "AesDerivedKeyParams", 6 | "AesGcmParams", 7 | "AesKeyAlgorithm", 8 | "AesKeyGenParams", 9 | "AutoFillBase", 10 | "AutoFillSection", 11 | "AutoFillAddressKind", 12 | "AutoFillNormalField", 13 | "AutoFillContactKind", 14 | "AutoFillContactField", 15 | "AutoFillField", 16 | "AutoFillCredentialField", 17 | "AutoFill", 18 | "BigInteger", 19 | "ClientQueryOptions", 20 | "ClientTypes", 21 | "CompositeOperationOrAuto", 22 | "ComputedKeyframe", 23 | "DisplayCaptureSurfaceType", 24 | "EcdhKeyDeriveParams", 25 | "EcdsaParams", 26 | "EcKeyAlgorithm", 27 | "EcKeyGenParams", 28 | "EcKeyImportParams", 29 | "EventListenerOrEventListenerObject", 30 | "HashAlgorithmIdentifier", 31 | "HkdfParams", 32 | "HmacImportParams", 33 | "HmacKeyAlgorithm", 34 | "HmacKeyGenParams", 35 | "ImageBitmapRenderingContextSettings", 36 | "Keyframe", 37 | "MutationRecordType", 38 | "NamedCurve", 39 | "OptionalPrefixToken", 40 | "OptionalPostfixToken", 41 | "Pbkdf2Params", 42 | "PropertyIndexedKeyframes", 43 | "RsaHashedImportParams", 44 | "RsaHashedKeyAlgorithm", 45 | "RsaHashedKeyGenParams", 46 | "RsaKeyAlgorithm", 47 | "RsaKeyGenParams", 48 | "RsaOaepParams", 49 | "RsaPssParams", 50 | "RTCCertificateExpiration", 51 | "RTCDtlsRole", 52 | "RTCIceCandidatePairStats", 53 | "RTCIceRole", 54 | "RTCInboundRtpStreamStats", 55 | "RTCOutboundRtpStreamStats", 56 | "RTCQualityLimitationReason", 57 | "RTCReceivedRtpStreamStats", 58 | "RTCRtpStreamStats", 59 | "RTCSentRtpStreamStats", 60 | "RTCStats", 61 | "RTCStatsIceCandidatePairState", 62 | "RTCStatsType", 63 | "RTCTransportStats", 64 | "Transferable", 65 | "ValueTypeMap", 66 | "VideoFacingModeEnum" 67 | ], 68 | "Worker": [ 69 | "AesCbcParams", 70 | "AesCtrParams", 71 | "AesDerivedKeyParams", 72 | "AesGcmParams", 73 | "AesKeyAlgorithm", 74 | "AesKeyGenParams", 75 | "BigInteger", 76 | "ClientTypes", 77 | "EcdhKeyDeriveParams", 78 | "EcdsaParams", 79 | "EcKeyGenParams", 80 | "EcKeyImportParams", 81 | "EventListenerOrEventListenerObject", 82 | "HashAlgorithmIdentifier", 83 | "HkdfParams", 84 | "HmacImportParams", 85 | "HmacKeyGenParams", 86 | "ImageBitmapRenderingContextSettings", 87 | "NamedCurve", 88 | "Pbkdf2Params", 89 | "RsaHashedImportParams", 90 | "RsaHashedKeyGenParams", 91 | "RsaKeyGenParams", 92 | "RsaOaepParams", 93 | "RsaPssParams", 94 | "Transferable", 95 | "ValueTypeMap" 96 | ], 97 | "Worklet": [ 98 | "EventListenerOrEventListenerObject", 99 | "Transferable", 100 | "ValueTypeMap" 101 | ] 102 | } 103 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@typescript/dom-lib-generator", 3 | "version": "0.0.1", 4 | "private": true, 5 | "description": "Provides TypeScript types for the latest web APIs.", 6 | "keywords": [ 7 | "typescript", 8 | "web", 9 | "api", 10 | "dom", 11 | "worker", 12 | "worklet" 13 | ], 14 | "scripts": { 15 | "build": "tsc && node ./lib/build.js", 16 | "baseline-accept": "cpx \"generated\\**\" baselines\\", 17 | "lint": "eslint --max-warnings 0 src deploy/*.js && tsc -p deploy/jsconfig.json", 18 | "lint-fix": "eslint --max-warnings 0 src deploy/*.js --fix", 19 | "test": "npm run build && npm run lint && node ./lib/test.js && node ./unittests/index.js", 20 | "changelog": "tsc && node ./lib/changelog.js", 21 | "ts-changelog": "node ./deploy/versionChangelog.js", 22 | "migrate": "node ./deploy/migrate.js", 23 | "version": "npm i && tsc && node ./lib/version.js", 24 | "generate": "npm run build && npm run baseline-accept" 25 | }, 26 | "author": { 27 | "name": "Kagami Sascha Rosylight", 28 | "email": "saschanaz@outlook.com", 29 | "url": "https://github.com/saschanaz" 30 | }, 31 | "contributors": [ 32 | "Microsoft Corp." 33 | ], 34 | "repository": { 35 | "type": "git", 36 | "url": "https://github.com/microsoft/TypeScript-DOM-lib-generator/" 37 | }, 38 | "license": "Apache-2.0", 39 | "type": "module", 40 | "devDependencies": { 41 | "@eslint/js": "^9", 42 | "@mdn/browser-compat-data": "^6.0.4", 43 | "@octokit/rest": "^21.0.0", 44 | "@types/node": "^22.2.0", 45 | "@types/prettier": "^3.0.0", 46 | "@types/webidl2": "^24.4.3", 47 | "@typescript-eslint/eslint-plugin": "^8", 48 | "@typescript-eslint/parser": "^8", 49 | "@webref/css": "^6.12.7", 50 | "@webref/elements": "^2.3.0", 51 | "@webref/events": "^1.11.3", 52 | "@webref/idl": "^3.46.1", 53 | "bcd-idl-mapper": "^2.3.0", 54 | "cpx2": "^8.0.0", 55 | "danger": "^13.0.4", 56 | "eslint": "^9.9.0", 57 | "eslint-config-prettier": "^10.0.1", 58 | "eslint-plugin-prettier": "^5.1.3", 59 | "globals": "^16.0.0", 60 | "jsonc-parser": "^3.2.1", 61 | "node-fetch": "^3.3.2", 62 | "prettier": "^3.2.5", 63 | "print-diff": "^2.0.0", 64 | "typescript": "^5.6.0-dev.20240806", 65 | "typescript-eslint": "^8", 66 | "webidl2": "^24.4.1" 67 | }, 68 | "overrides": { 69 | "typescript@*": "$typescript" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/build.ts: -------------------------------------------------------------------------------- 1 | import * as Browser from "./build/types.js"; 2 | import { promises as fs } from "fs"; 3 | import { merge, resolveExposure, arrayToMap } from "./build/helpers.js"; 4 | import { type CompilerBehavior, emitWebIdl } from "./build/emitter.js"; 5 | import { convert } from "./build/widlprocess.js"; 6 | import { getExposedTypes } from "./build/expose.js"; 7 | import { 8 | getDeprecationData, 9 | getDocsData, 10 | getRemovalData, 11 | } from "./build/bcd.js"; 12 | import { getInterfaceElementMergeData } from "./build/webref/elements.js"; 13 | import { getInterfaceToEventMap } from "./build/webref/events.js"; 14 | import { getWebidls } from "./build/webref/idl.js"; 15 | import jsonc from "jsonc-parser"; 16 | import { generateDescription } from "./build/mdn-comments.js"; 17 | 18 | function mergeNamesakes(filtered: Browser.WebIdl) { 19 | const targets = [ 20 | ...Object.values(filtered.interfaces!.interface), 21 | ...Object.values(filtered.mixins!.mixin), 22 | ...filtered.namespaces!, 23 | ]; 24 | for (const i of targets) { 25 | if (!i.properties || !i.properties.namesakes) { 26 | continue; 27 | } 28 | const { property } = i.properties!; 29 | for (const [prop] of Object.values(i.properties.namesakes)) { 30 | if (prop && !(prop.name in property)) { 31 | property[prop.name] = prop; 32 | } 33 | } 34 | } 35 | } 36 | 37 | interface EmitOptions { 38 | global: string[]; 39 | name: string; 40 | outputFolder: URL; 41 | compilerBehavior: CompilerBehavior; 42 | } 43 | 44 | async function emitFlavor( 45 | webidl: Browser.WebIdl, 46 | forceKnownTypes: Set, 47 | options: EmitOptions, 48 | ) { 49 | const exposed = getExposedTypes(webidl, options.global, forceKnownTypes); 50 | mergeNamesakes(exposed); 51 | exposed.events = webidl.events; 52 | 53 | const result = emitWebIdl( 54 | exposed, 55 | options.global[0], 56 | "", 57 | options.compilerBehavior, 58 | ); 59 | await fs.writeFile( 60 | new URL(`${options.name}.generated.d.ts`, options.outputFolder), 61 | result, 62 | ); 63 | 64 | const iterators = emitWebIdl( 65 | exposed, 66 | options.global[0], 67 | "sync", 68 | options.compilerBehavior, 69 | ); 70 | await fs.writeFile( 71 | new URL(`${options.name}.iterable.generated.d.ts`, options.outputFolder), 72 | iterators, 73 | ); 74 | 75 | const asyncIterators = emitWebIdl( 76 | exposed, 77 | options.global[0], 78 | "async", 79 | options.compilerBehavior, 80 | ); 81 | await fs.writeFile( 82 | new URL( 83 | `${options.name}.asynciterable.generated.d.ts`, 84 | options.outputFolder, 85 | ), 86 | asyncIterators, 87 | ); 88 | } 89 | 90 | async function emitDom() { 91 | const inputFolder = new URL("../inputfiles/", import.meta.url); 92 | const outputFolder = new URL("../generated/", import.meta.url); 93 | 94 | const overriddenItems = await readInputJSON("overridingTypes.jsonc"); 95 | const addedItems = await readInputJSON("addedTypes.jsonc"); 96 | const comments = await readInputJSON("comments.json"); 97 | const deprecatedInfo = await readInputJSON("deprecatedMessage.json"); 98 | const documentationFromMDN = await generateDescription(); 99 | const removedItems = await readInputJSON("removedTypes.jsonc"); 100 | 101 | async function readInputJSON(filename: string) { 102 | const content = await fs.readFile(new URL(filename, inputFolder), "utf8"); 103 | return jsonc.parse(content); 104 | } 105 | 106 | const widlStandardTypes = ( 107 | await Promise.all([...(await getWebidls()).entries()].map(convertWidl)) 108 | ).filter((i) => i) as ReturnType[]; 109 | 110 | async function convertWidl([shortName, idl]: string[]) { 111 | let commentsMap: Record; 112 | try { 113 | commentsMap = await readInputJSON(`idl/${shortName}.commentmap.json`); 114 | } catch { 115 | commentsMap = {}; 116 | } 117 | commentCleanup(commentsMap); 118 | const result = convert(idl, commentsMap); 119 | return result; 120 | } 121 | 122 | function commentCleanup(commentsMap: Record) { 123 | for (const key in commentsMap) { 124 | // Filters out phrases for nested comments as we retargets them: 125 | // "This operation receives a dictionary, which has these members:" 126 | commentsMap[key] = commentsMap[key].replace(/[,.][^,.]+:$/g, "."); 127 | } 128 | } 129 | 130 | function mergeApiDescriptions( 131 | idl: Browser.WebIdl, 132 | descriptions: Record, 133 | ) { 134 | const namespaces = arrayToMap( 135 | idl.namespaces!, 136 | (i) => i.name, 137 | (i) => i, 138 | ); 139 | for (const [key, value] of Object.entries(descriptions)) { 140 | const target = idl.interfaces!.interface[key] || namespaces[key]; 141 | if (target) { 142 | target.comment = value; 143 | } 144 | } 145 | return idl; 146 | } 147 | 148 | function mergeDeprecatedMessage( 149 | idl: Browser.WebIdl, 150 | descriptions: Record, 151 | ) { 152 | const namespaces = arrayToMap( 153 | idl.namespaces!, 154 | (i) => i.name, 155 | (i) => i, 156 | ); 157 | for (const [key, value] of Object.entries(descriptions)) { 158 | const target = idl.interfaces!.interface[key] || namespaces[key]; 159 | if (target) { 160 | target.deprecated = value; 161 | } 162 | } 163 | return idl; 164 | } 165 | 166 | /// Load the input file 167 | let webidl: Browser.WebIdl = { 168 | events: await getInterfaceToEventMap(), 169 | }; 170 | 171 | for (const w of widlStandardTypes) { 172 | webidl = merge(webidl, w.browser, true); 173 | } 174 | for (const w of widlStandardTypes) { 175 | for (const partial of w.partialInterfaces) { 176 | // Fallback to mixins before every spec migrates to `partial interface mixin`. 177 | const base = 178 | webidl.interfaces!.interface[partial.name] || 179 | webidl.mixins!.mixin[partial.name]; 180 | if (base) { 181 | if (base.exposed) resolveExposure(partial, base.exposed); 182 | merge(base.constants, partial.constants, true); 183 | merge(base.methods, partial.methods, true); 184 | merge(base.properties, partial.properties, true); 185 | } 186 | } 187 | for (const partial of w.partialMixins) { 188 | const base = webidl.mixins!.mixin[partial.name]; 189 | if (base) { 190 | if (base.exposed) resolveExposure(partial, base.exposed); 191 | merge(base.constants, partial.constants, true); 192 | merge(base.methods, partial.methods, true); 193 | merge(base.properties, partial.properties, true); 194 | } 195 | } 196 | for (const partial of w.partialDictionaries) { 197 | const base = webidl.dictionaries!.dictionary[partial.name]; 198 | if (base) { 199 | merge(base.members, partial.members, true); 200 | } 201 | } 202 | for (const partial of w.partialNamespaces) { 203 | const base = webidl.namespaces?.find((n) => n.name === partial.name); 204 | if (base) { 205 | if (base.exposed) resolveExposure(partial, base.exposed); 206 | merge(base.methods, partial.methods, true); 207 | merge(base.properties, partial.properties, true); 208 | } 209 | } 210 | for (const include of w.includes) { 211 | const target = webidl.interfaces!.interface[include.target]; 212 | if (target) { 213 | if (!target.implements) { 214 | target.implements = [include.includes]; 215 | } else { 216 | target.implements.push(include.includes); 217 | } 218 | } 219 | } 220 | } 221 | webidl = merge(webidl, await getInterfaceElementMergeData()); 222 | 223 | webidl = merge(webidl, getDeprecationData(webidl)); 224 | webidl = merge(webidl, getRemovalData(webidl)); 225 | webidl = merge(webidl, getDocsData(webidl)); 226 | webidl = prune(webidl, removedItems); 227 | webidl = mergeApiDescriptions(webidl, documentationFromMDN); 228 | webidl = merge(webidl, addedItems); 229 | webidl = merge(webidl, overriddenItems); 230 | webidl = merge(webidl, comments); 231 | webidl = mergeDeprecatedMessage(webidl, deprecatedInfo); 232 | for (const name in webidl.interfaces!.interface) { 233 | const i = webidl.interfaces!.interface[name]; 234 | if (i.overrideExposed) { 235 | resolveExposure(i, i.overrideExposed!, true); 236 | } 237 | } 238 | 239 | const transferables = Object.values( 240 | webidl.interfaces?.interface ?? {}, 241 | ).filter((i) => i.transferable); 242 | 243 | webidl = merge(webidl, { 244 | typedefs: { 245 | typedef: [ 246 | { 247 | name: "Transferable", 248 | type: [ 249 | ...transferables.map((v) => ({ type: v.name })), 250 | { type: "ArrayBuffer" }, 251 | ], 252 | }, 253 | ], 254 | }, 255 | }); 256 | 257 | const knownTypes = await readInputJSON("knownTypes.json"); 258 | 259 | interface Variation { 260 | outputFolder: URL; 261 | compilerBehavior: CompilerBehavior; 262 | } 263 | 264 | const emitVariations: Variation[] = [ 265 | // ts5.7 (and later) 266 | // - introduced generic typed arrays over `ArrayBufferLike` 267 | { 268 | outputFolder, 269 | compilerBehavior: { 270 | useIteratorObject: true, 271 | allowUnrelatedSetterType: true, 272 | useGenericTypedArrays: true, 273 | }, 274 | }, 275 | // ts5.6 276 | // - introduced support for `IteratorObject`/Iterator helpers and unrelated setter types 277 | { 278 | outputFolder: new URL("./ts5.6/", outputFolder), 279 | compilerBehavior: { 280 | useIteratorObject: true, 281 | allowUnrelatedSetterType: true, 282 | }, 283 | }, 284 | // ts5.5 (and earlier) 285 | { 286 | outputFolder: new URL("./ts5.5/", outputFolder), 287 | compilerBehavior: {}, // ts5.5 does not support `IteratorObject` or unrelated setter types 288 | }, 289 | ]; 290 | 291 | for (const { outputFolder, compilerBehavior } of emitVariations) { 292 | // Create output folder 293 | await fs.mkdir(outputFolder, { 294 | // Doesn't need to be recursive, but this helpfully ignores EEXIST 295 | recursive: true, 296 | }); 297 | 298 | emitFlavor(webidl, new Set(knownTypes.Window), { 299 | name: "dom", 300 | global: ["Window"], 301 | outputFolder, 302 | compilerBehavior, 303 | }); 304 | emitFlavor(webidl, new Set(knownTypes.Worker), { 305 | name: "webworker", 306 | global: ["Worker", "DedicatedWorker", "SharedWorker", "ServiceWorker"], 307 | outputFolder, 308 | compilerBehavior, 309 | }); 310 | emitFlavor(webidl, new Set(knownTypes.Worker), { 311 | name: "sharedworker", 312 | global: ["SharedWorker", "Worker"], 313 | outputFolder, 314 | compilerBehavior, 315 | }); 316 | emitFlavor(webidl, new Set(knownTypes.Worker), { 317 | name: "serviceworker", 318 | global: ["ServiceWorker", "Worker"], 319 | outputFolder, 320 | compilerBehavior, 321 | }); 322 | emitFlavor(webidl, new Set(knownTypes.Worklet), { 323 | name: "audioworklet", 324 | global: ["AudioWorklet", "Worklet"], 325 | outputFolder, 326 | compilerBehavior, 327 | }); 328 | } 329 | 330 | function prune( 331 | obj: Browser.WebIdl, 332 | template: Partial, 333 | ): Browser.WebIdl { 334 | return filterByNull(obj, template); 335 | 336 | function filterByNull(obj: any, template: any) { 337 | if (!template) return obj; 338 | const filtered = Array.isArray(obj) ? obj.slice(0) : { ...obj }; 339 | for (const k in template) { 340 | if (!obj[k]) { 341 | console.warn( 342 | `removedTypes.json has a redundant field ${k} in ${JSON.stringify( 343 | template, 344 | ).slice(0, 100)}`, 345 | ); 346 | } else if (Array.isArray(template[k])) { 347 | if (!Array.isArray(obj[k])) { 348 | throw new Error( 349 | `Removal template ${k} is an array but the original field is not`, 350 | ); 351 | } 352 | // template should include strings 353 | filtered[k] = obj[k].filter((item: any) => { 354 | const name = typeof item === "string" ? item : item.name; 355 | return !template[k].includes(name); 356 | }); 357 | if (filtered[k].length !== obj[k].length - template[k].length) { 358 | const differences = template[k].filter( 359 | (t: any) => !obj[k].includes(t), 360 | ); 361 | console.warn( 362 | `removedTypes.json has redundant array items: ${differences}`, 363 | ); 364 | } 365 | } else if (template[k] !== null) { 366 | filtered[k] = filterByNull(obj[k], template[k]); 367 | } else { 368 | if (obj[k].exposed === "") { 369 | console.warn( 370 | `removedTypes.json removes ${k} that has already been disabled by BCD.`, 371 | ); 372 | } 373 | delete filtered[k]; 374 | } 375 | } 376 | return filtered; 377 | } 378 | } 379 | } 380 | 381 | await emitDom(); 382 | -------------------------------------------------------------------------------- /src/build/bcd.ts: -------------------------------------------------------------------------------- 1 | import * as Browser from "./types"; 2 | import { 3 | CompatStatement, 4 | SimpleSupportStatement, 5 | SupportBlock, 6 | } from "@mdn/browser-compat-data/types"; 7 | import { forceKeepAlive } from "./bcd/keep-alive.js"; 8 | import { mapToBcdCompat } from "./bcd/mapper.js"; 9 | import { hasStableImplementation } from "./bcd/stable.js"; 10 | 11 | function hasMultipleImplementations(support: SupportBlock, prefix?: string) { 12 | const hasStableImpl = ( 13 | browser: SimpleSupportStatement | SimpleSupportStatement[] | undefined, 14 | ) => hasStableImplementation(browser, prefix); 15 | let count = 0; 16 | if (hasStableImpl(support.chrome) || hasStableImpl(support.chrome_android)) { 17 | count += 1; 18 | } 19 | if ( 20 | hasStableImpl(support.firefox) || 21 | hasStableImpl(support.firefox_android) 22 | ) { 23 | count += 1; 24 | } 25 | if (hasStableImpl(support.safari) || hasStableImpl(support.safari_ios)) { 26 | count += 1; 27 | } 28 | return count >= 2; 29 | } 30 | 31 | function isSuitable( 32 | key: string, 33 | compat?: CompatStatement, 34 | parentKey?: string, 35 | prefix?: string, 36 | ) { 37 | const forceAlive = parentKey 38 | ? forceKeepAlive[parentKey]?.includes(key) 39 | : !!forceKeepAlive[key]; 40 | if (compat && hasMultipleImplementations(compat.support, prefix)) { 41 | if (forceAlive) { 42 | if (parentKey) { 43 | console.warn(`Redundant forceKeepAlive item: ${parentKey}#${key}`); 44 | } else if (!forceKeepAlive[key].length) { 45 | console.warn(`Redundant forceKeepAlive item: ${key}`); 46 | } 47 | } 48 | return true; 49 | } 50 | return forceAlive; 51 | } 52 | 53 | export function getRemovalData(webidl: Browser.WebIdl): Browser.WebIdl { 54 | return mapToBcdCompat(webidl, ({ key, parentKey, compat, mixin }) => { 55 | // Allow all mixins here, but not their members. 56 | // Empty mixins created by this will be managed by exposed.ts. 57 | // (It's better to manage mixins there as mixins can also conditionally be empty by exposure settings) 58 | if (mixin && !parentKey) { 59 | return; 60 | } 61 | if (isSuitable(key, compat, parentKey)) { 62 | return; 63 | } 64 | return { exposed: "" }; 65 | }) as Browser.WebIdl; 66 | } 67 | 68 | export function getDeprecationData(webidl: Browser.WebIdl): Browser.WebIdl { 69 | return mapToBcdCompat(webidl, ({ compat }) => { 70 | if (compat?.status?.deprecated) { 71 | return { deprecated: 1 }; 72 | } else if (compat?.status?.preferred_name) { 73 | return { 74 | deprecated: `This is a legacy alias of \`${compat.status.preferred_name}\`.`, 75 | }; 76 | } 77 | }) as Browser.WebIdl; 78 | } 79 | 80 | export function getDocsData(webidl: Browser.WebIdl): Browser.WebIdl { 81 | return mapToBcdCompat(webidl, ({ compat }) => { 82 | if (compat?.mdn_url) { 83 | return { mdnUrl: compat.mdn_url }; 84 | } 85 | }) as Browser.WebIdl; 86 | } 87 | -------------------------------------------------------------------------------- /src/build/bcd/keep-alive.ts: -------------------------------------------------------------------------------- 1 | export const forceKeepAlive: Record = { 2 | // Things that are incorrectly reported as unsupported. 3 | // These should be filed to https://github.com/mdn/browser-compat-data/issues 4 | ContactAddress: [ 5 | "country", 6 | "addressLine", 7 | "region", 8 | "city", 9 | "dependentLocality", 10 | "postalCode", 11 | "sortingCode", 12 | "organization", 13 | "recipient", 14 | "phone", 15 | "toJSON", 16 | ], 17 | CSSStyleDeclaration: [ 18 | // https://github.com/mdn/browser-compat-data/commit/ebabd27460a306d6de80107b7e3c62be99ecd13c#r135144607 19 | "webkitMaskComposite", 20 | ], 21 | GlobalEventHandlers: [ 22 | "onwebkitanimationend", 23 | "onwebkitanimationiteration", 24 | "onwebkitanimationstart", 25 | ], 26 | IDBDatabase: [ 27 | // BCD unexpectedly is removing valid event data 28 | // https://github.com/mdn/browser-compat-data/issues/15345 29 | "onabort", 30 | "onerror", 31 | ], 32 | ShadowRoot: [ 33 | // BCD unexpectedly is removing valid event data 34 | // https://github.com/mdn/browser-compat-data/issues/15345 35 | "onslotchange", 36 | ], 37 | XMLHttpRequestEventTarget: [ 38 | // BCD unexpectedly is removing valid event data 39 | // https://github.com/mdn/browser-compat-data/issues/15345 40 | "onabort", 41 | "onerror", 42 | "onload", 43 | "onloadend", 44 | "onloadstart", 45 | "onprogress", 46 | "ontimeout", 47 | ], 48 | }; 49 | -------------------------------------------------------------------------------- /src/build/bcd/mapper.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BrowserName, 3 | CompatStatement, 4 | Identifier, 5 | SimpleSupportStatement, 6 | } from "bcd-idl-mapper"; 7 | import api from "bcd-idl-mapper"; 8 | import * as Browser from "../types"; 9 | import { filterMapRecord, isEmptyRecord } from "../utils/record.js"; 10 | import { mapDefined } from "../helpers.js"; 11 | import { hasStableImplementation } from "./stable.js"; 12 | 13 | interface DataToMap { 14 | key: string; 15 | compat?: CompatStatement; 16 | webkit?: boolean; 17 | mixin: boolean; 18 | parentKey?: string; 19 | } 20 | 21 | function mergeCompatStatements(data?: Identifier): CompatStatement | undefined { 22 | if (!data) { 23 | return; 24 | } 25 | if (data?.__compat) { 26 | return data.__compat; 27 | } 28 | 29 | // Some items have no top level __compat and instead have contexts with compat data for each 30 | 31 | const statements = Object.values(data as Record) 32 | .map((d) => d.__compat) 33 | .filter((n) => n) as CompatStatement[]; 34 | 35 | const base = Object.fromEntries( 36 | Object.keys(statements[0].support).map((key) => { 37 | return [key, [] as SimpleSupportStatement[]]; 38 | }), 39 | ); 40 | 41 | for (const statement of statements) { 42 | for (const key of Object.keys(statement.support) as BrowserName[]) { 43 | const support = statement.support[key]; 44 | if (support && hasStableImplementation(support)) { 45 | if (!base[key]) { 46 | base[key] = []; // some support field is not everywhere e.g. deno 47 | } 48 | base[key].push(...(Array.isArray(support) ? support : [support])); 49 | } 50 | } 51 | } 52 | 53 | return { ...statements[0], support: base }; 54 | } 55 | 56 | function mapInterfaceLike( 57 | name: string, 58 | i: Browser.Interface, 59 | mapper: (data: DataToMap) => any, 60 | ) { 61 | const data = i.mixin 62 | ? api.__mixins[name] 63 | : i.legacyNamespace 64 | ? api[i.legacyNamespace][name] 65 | : api[name]; 66 | const intCompat = data?.__compat; 67 | const mapped = mapper({ key: name, compat: intCompat, mixin: !!i.mixin }); 68 | if (!data) { 69 | if (mapped) { 70 | return { name: i.name, ...mapped }; 71 | } 72 | return; 73 | } 74 | const result = { ...mapped }; 75 | 76 | const recordMapper = (key: string) => { 77 | const compat = mergeCompatStatements(data[key]); 78 | return mapper({ 79 | key, 80 | parentKey: name, 81 | compat, 82 | mixin: !!i.mixin, 83 | }); 84 | }; 85 | 86 | const methods = filterMapRecord(i.methods?.method, recordMapper, i.namespace); 87 | const properties = filterMapRecord( 88 | i.properties?.property, 89 | recordMapper, 90 | i.namespace, 91 | ); 92 | 93 | if (i.iterator) { 94 | const iteratorKey = i.iterator.async ? "@@asyncIterator" : "@@iterator"; 95 | // BCD often doesn't have an @@iterator entry, but it does usually have an entry 96 | // for iterable methods such as values(). Use that as a fallback. 97 | // See also: https://github.com/mdn/browser-compat-data/issues/6367 98 | const iteratorCompat = mergeCompatStatements( 99 | data[iteratorKey] ?? data["values"], 100 | ); 101 | const iteratorMapped = mapper({ 102 | key: iteratorKey, 103 | parentKey: name, 104 | compat: iteratorCompat, 105 | mixin: !!i.mixin, 106 | }); 107 | if (iteratorMapped !== undefined) { 108 | result.iterator = iteratorMapped; 109 | } 110 | } 111 | 112 | if (!isEmptyRecord(methods)) { 113 | result.methods = { method: methods! }; 114 | } 115 | if (!isEmptyRecord(properties)) { 116 | result.properties = { property: properties! }; 117 | } 118 | if (!isEmptyRecord(result)) { 119 | return { name: i.name, ...result }; 120 | } 121 | } 122 | 123 | export function mapToBcdCompat( 124 | webidl: Browser.WebIdl, 125 | mapper: (data: DataToMap) => any, 126 | ): Browser.WebIdl | undefined { 127 | const map = (name: string, i: Browser.Interface) => 128 | mapInterfaceLike(name, i, mapper); 129 | 130 | const interfaces = filterMapRecord(webidl.interfaces?.interface, map); 131 | const mixins = filterMapRecord(webidl.mixins?.mixin, map); 132 | const namespaces = mapDefined(webidl.namespaces, (n) => 133 | mapInterfaceLike(n.name, n, mapper), 134 | ); 135 | if ( 136 | !isEmptyRecord(interfaces) || 137 | !isEmptyRecord(mixins) || 138 | !isEmptyRecord(namespaces) 139 | ) { 140 | return { 141 | interfaces: interfaces && { interface: interfaces }, 142 | mixins: mixins && { mixin: mixins }, 143 | namespaces, 144 | }; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/build/bcd/stable.ts: -------------------------------------------------------------------------------- 1 | import { SimpleSupportStatement } from "@mdn/browser-compat-data/types"; 2 | 3 | export function hasStableImplementation( 4 | browser: SimpleSupportStatement | SimpleSupportStatement[] | undefined, 5 | prefix?: string, 6 | ): boolean { 7 | if (!browser) { 8 | return false; 9 | } 10 | const latest = !Array.isArray(browser) 11 | ? browser 12 | : browser.find((i) => i.prefix === prefix); // first one if no prefix 13 | if (!latest) { 14 | return false; 15 | } 16 | return ( 17 | !!latest.version_added && 18 | // "preview" means BCD has no idea about whether it will ride the train 19 | // https://github.com/mdn/browser-compat-data/issues/12344 20 | latest.version_added !== "preview" && 21 | !latest.version_removed && 22 | !latest.flags && 23 | latest.prefix === prefix && 24 | !latest.alternative_name 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/build/browser-specs.ts: -------------------------------------------------------------------------------- 1 | import { createRequire } from "module"; 2 | 3 | const require = createRequire(import.meta.url); 4 | const browserSpecs = require("browser-specs") as any[]; 5 | 6 | export function getLatestSpecNames(): string[] { 7 | return [ 8 | ...new Set(browserSpecs.map((spec) => spec.series.shortname)), 9 | ...browserSpecs 10 | .filter((spec) => spec.seriesComposition === "delta") 11 | .map((spec) => spec.shortname), 12 | 13 | // https://wiki.whatwg.org/wiki/DOM_XSLTProcessor 14 | "xsltprocessor", 15 | ]; 16 | } 17 | -------------------------------------------------------------------------------- /src/build/expose.ts: -------------------------------------------------------------------------------- 1 | import * as Browser from "./types.js"; 2 | import { 3 | getEmptyWebIDL, 4 | deepFilter, 5 | exposesTo, 6 | followTypeReferences, 7 | filterProperties, 8 | mapToArray, 9 | arrayToMap, 10 | } from "./helpers.js"; 11 | import { isEmptyRecord } from "./utils/record.js"; 12 | 13 | class LoggedSet extends Set { 14 | private unvisited: Set; 15 | 16 | constructor(set: Set) { 17 | super(set); 18 | this.unvisited = new Set(set); 19 | } 20 | has(value: string) { 21 | this.unvisited.delete(value); 22 | return super.has(value); 23 | } 24 | unvisitedValues() { 25 | return this.unvisited.values(); 26 | } 27 | } 28 | 29 | export function getExposedTypes( 30 | webidl: Browser.WebIdl, 31 | target: string[], 32 | forceKnownTypes: Set, 33 | ): Browser.WebIdl { 34 | const forceKnownTypesLogged = new LoggedSet(forceKnownTypes); 35 | 36 | const unexposedTypes = new Set(); 37 | const filtered = getEmptyWebIDL(); 38 | if (webidl.interfaces) { 39 | filtered.interfaces!.interface = deepFilter( 40 | webidl.interfaces.interface, 41 | (o) => exposesTo(o, target), 42 | ); 43 | const unexposedInterfaces = mapToArray(webidl.interfaces.interface).filter( 44 | (i) => !exposesTo(i, target), 45 | ); 46 | for (const i of unexposedInterfaces) { 47 | unexposedTypes.add(i.name); 48 | } 49 | } 50 | if (webidl.namespaces) { 51 | filtered.namespaces = deepFilter(webidl.namespaces, (o) => 52 | exposesTo(o, target), 53 | ); 54 | } 55 | 56 | if (webidl.mixins) { 57 | const allIncludes = Object.values(filtered.interfaces?.interface || {}) 58 | .map((i) => i.implements || []) 59 | .flat(); 60 | const mixins = deepFilter(webidl.mixins.mixin, (o) => exposesTo(o, target)); 61 | filtered.mixins!.mixin = filterProperties( 62 | mixins, 63 | (m: Browser.Interface) => 64 | allIncludes.includes(m.name) && !isEmptyMixin(m), 65 | ); 66 | for (const value of Object.values(filtered.interfaces!.interface || {})) { 67 | if (value.implements) { 68 | value.implements = value.implements.filter( 69 | (i) => !!filtered.mixins!.mixin[i], 70 | ); 71 | } 72 | } 73 | } 74 | 75 | const knownIDLTypes = new Set([ 76 | ...followTypeReferences(webidl, filtered.interfaces!.interface), 77 | ...followTypeReferences( 78 | webidl, 79 | arrayToMap( 80 | filtered.namespaces!, 81 | (i) => i.name, 82 | (i) => i, 83 | ), 84 | ), 85 | ]); 86 | const isKnownName = (o: { name: string }) => 87 | knownIDLTypes.has(o.name) || forceKnownTypesLogged.has(o.name); 88 | 89 | if (webidl.typedefs) { 90 | const referenced = webidl.typedefs.typedef.filter( 91 | (t) => knownIDLTypes.has(t.name) || forceKnownTypesLogged.has(t.name), 92 | ); 93 | const { exposed, removed } = filterTypedefs(referenced, unexposedTypes); 94 | removed.forEach((s) => unexposedTypes.add(s)); 95 | filtered.typedefs!.typedef = exposed; 96 | } 97 | 98 | if (webidl.callbackFunctions) 99 | filtered.callbackFunctions!.callbackFunction = filterProperties( 100 | webidl.callbackFunctions!.callbackFunction, 101 | isKnownName, 102 | ); 103 | if (webidl.callbackInterfaces) 104 | filtered.callbackInterfaces!.interface = filterProperties( 105 | webidl.callbackInterfaces!.interface, 106 | isKnownName, 107 | ); 108 | if (webidl.dictionaries) 109 | filtered.dictionaries!.dictionary = filterProperties( 110 | webidl.dictionaries.dictionary, 111 | isKnownName, 112 | ); 113 | if (webidl.enums) 114 | filtered.enums!.enum = filterProperties(webidl.enums.enum, isKnownName); 115 | 116 | for (const unvisited of forceKnownTypesLogged.unvisitedValues()) { 117 | console.warn(`${unvisited} is redundant in knownTypes.json (${target})`); 118 | } 119 | 120 | return deepFilterUnexposedTypes(filtered, unexposedTypes); 121 | } 122 | 123 | /** 124 | * Filters unexposed types out from typedefs and 125 | * removes typedefs that only contains unexposed type names 126 | * @param typedefs target typedef array 127 | * @param unexposedTypes type names to be filtered out 128 | */ 129 | function filterTypedefs( 130 | typedefs: Browser.TypeDef[], 131 | unexposedTypes: Set, 132 | ): { exposed: Browser.TypeDef[]; removed: Set } { 133 | const exposed: Browser.TypeDef[] = []; 134 | const removed = new Set(); 135 | 136 | typedefs.forEach(filterTypedef); 137 | if (removed.size) { 138 | const result = filterTypedefs(exposed, removed); 139 | result.removed.forEach((s) => removed.add(s)); 140 | return { exposed: result.exposed, removed }; 141 | } else { 142 | return { exposed, removed }; 143 | } 144 | 145 | function filterTypedef(typedef: Browser.TypeDef) { 146 | if (typedef.overrideType) { 147 | exposed.push(typedef); 148 | } else if (Array.isArray(typedef.type)) { 149 | const filteredType = filterUnexposedTypeFromUnion( 150 | typedef.type, 151 | unexposedTypes, 152 | ); 153 | if (!filteredType.length) { 154 | removed.add(typedef.name); 155 | } else { 156 | exposed.push({ ...typedef, type: flattenType(filteredType) }); 157 | } 158 | } else if (unexposedTypes.has(typedef.type)) { 159 | removed.add(typedef.name); 160 | } else { 161 | exposed.push(typedef); 162 | } 163 | } 164 | } 165 | 166 | /** 167 | * Filters out unexposed type names from union types and optional function arguments 168 | * @param webidl target types 169 | * @param unexposedTypes type names to be filtered out 170 | */ 171 | function deepFilterUnexposedTypes( 172 | webidl: Browser.WebIdl, 173 | unexposedTypes: Set, 174 | ) { 175 | return deepClone(webidl, (o) => { 176 | if (Array.isArray(o.type)) { 177 | return { 178 | ...o, 179 | type: filterUnexposedTypeFromUnion(o.type, unexposedTypes), 180 | }; 181 | } 182 | if (!o.overrideSignatures && Array.isArray(o.signature)) { 183 | return { 184 | ...o, 185 | signature: o.signature.map(filterUnknownTypeFromSignature), 186 | }; 187 | } 188 | if (o.members) { 189 | return filterUnknownTypeFromDictionary(o); 190 | } 191 | // TODO: Support filtering dictionary members 192 | }); 193 | 194 | function filterUnknownTypeFromSignature(signature: Browser.Signature) { 195 | if (!signature.param) { 196 | return signature; 197 | } 198 | const param: Browser.Param[] = []; 199 | for (const p of signature.param) { 200 | const types = Array.isArray(p.type) ? p.type : [p]; 201 | const filtered = filterUnexposedTypeFromUnion(types, unexposedTypes); 202 | if (filtered.length >= 1) { 203 | param.push({ ...p, type: flattenType(filtered) }); 204 | } else if (!p.optional) { 205 | throw new Error(`A non-optional parameter has unknown type: ${p.type}`); 206 | } else { 207 | // safe to skip 208 | break; 209 | } 210 | } 211 | return { ...signature, param }; 212 | } 213 | 214 | function filterUnknownTypeFromDictionary(dictionary: Browser.Dictionary) { 215 | const result: Record = {}; 216 | for (const member of Object.values(dictionary.members.member)) { 217 | const filtered = filterUnexposedType(member, unexposedTypes); 218 | if (filtered) { 219 | result[member.name] = filtered; 220 | } 221 | } 222 | return { ...dictionary, members: { member: result } }; 223 | } 224 | } 225 | 226 | function filterUnexposedType( 227 | type: T, 228 | unexposedTypes: Set, 229 | ) { 230 | if (Array.isArray(type.type)) { 231 | const filteredUnion = filterUnexposedTypeFromUnion( 232 | type.type, 233 | unexposedTypes, 234 | ); 235 | if (filteredUnion.length) { 236 | return { ...type, type: flattenType(filteredUnion) }; 237 | } 238 | } else if (type.overrideType || !unexposedTypes.has(type.type)) { 239 | return type; 240 | } 241 | } 242 | 243 | function filterUnexposedTypeFromUnion( 244 | union: Browser.Typed[], 245 | unexposedTypes: Set, 246 | ): Browser.Typed[] { 247 | const result: Browser.Typed[] = []; 248 | for (const type of union) { 249 | const filtered = filterUnexposedType(type, unexposedTypes); 250 | if (filtered) { 251 | result.push(filtered); 252 | } 253 | } 254 | return result; 255 | } 256 | 257 | function deepClone(o: T, custom: (o: any) => any): T { 258 | if (!o || typeof o !== "object") { 259 | return o; 260 | } 261 | if (Array.isArray(o)) { 262 | return o.map((v) => deepClone(v, custom)) as any as T; 263 | } 264 | const mapped = custom(o); 265 | if (mapped !== undefined) { 266 | return mapped; 267 | } 268 | const clone: any = {}; 269 | for (const key of Object.getOwnPropertyNames(o)) { 270 | clone[key] = deepClone((o as any)[key], custom); 271 | } 272 | return clone; 273 | } 274 | 275 | function flattenType(type: Browser.Typed[]) { 276 | if (type.length > 1) { 277 | return type; 278 | } else if (type.length === 1) { 279 | return type[0].type; 280 | } 281 | throw new Error("Cannot process empty union type"); 282 | } 283 | 284 | function isEmptyMixin(i?: Browser.Interface) { 285 | return ( 286 | !!i?.mixin && 287 | isEmptyRecord(i.properties?.property) && 288 | isEmptyRecord(i.methods?.method) && 289 | isEmptyRecord(i.constants?.constant) && 290 | !i.anonymousMethods?.method.length && 291 | !i.events?.event.length 292 | ); 293 | } 294 | -------------------------------------------------------------------------------- /src/build/helpers.ts: -------------------------------------------------------------------------------- 1 | import * as Browser from "./types.js"; 2 | 3 | // Extended types used but not defined in the spec 4 | export const arrayBufferViewTypes = new Set([ 5 | "ArrayBufferView", 6 | "DataView", 7 | "Int8Array", 8 | "Uint8Array", 9 | "Int16Array", 10 | "Uint16Array", 11 | "Uint8ClampedArray", 12 | "Int32Array", 13 | "Uint32Array", 14 | "Float32Array", 15 | "Float64Array", 16 | ]); 17 | export const bufferSourceTypes = new Set([ 18 | "ArrayBuffer", 19 | "SharedArrayBuffer", 20 | ...arrayBufferViewTypes, 21 | ]); 22 | export const integerTypes = new Set([ 23 | "byte", 24 | "octet", 25 | "short", 26 | "unsigned short", 27 | "long", 28 | "unsigned long", 29 | "long long", 30 | "unsigned long long", 31 | ]); 32 | export const stringTypes = new Set([ 33 | "ByteString", 34 | "DOMString", 35 | "USVString", 36 | "CSSOMString", 37 | ]); 38 | // Trusted Types https://w3c.github.io/trusted-types/dist/spec/ 39 | export const trustedStringTypes = new Set([ 40 | "HTMLString", 41 | "ScriptString", 42 | "ScriptURLString", 43 | ]); 44 | const floatTypes = new Set([ 45 | "float", 46 | "unrestricted float", 47 | "double", 48 | "unrestricted double", 49 | ]); 50 | const sameTypes = new Set([ 51 | "any", 52 | "boolean", 53 | "Date", 54 | "Function", 55 | "Promise", 56 | "PromiseLike", 57 | "undefined", 58 | "void", 59 | ]); 60 | export const baseTypeConversionMap = new Map([ 61 | ...[...bufferSourceTypes].map((type) => [type, type] as const), 62 | ...[...integerTypes, ...floatTypes].map((type) => [type, "number"] as const), 63 | ...[...stringTypes, ...trustedStringTypes].map( 64 | (type) => [type, "string"] as const, 65 | ), 66 | ...[...sameTypes].map((type) => [type, type] as const), 67 | ["object", "any"], 68 | ["sequence", "Array"], 69 | ["ObservableArray", "Array"], 70 | ["record", "Record"], 71 | ["FrozenArray", "ReadonlyArray"], 72 | ["EventHandler", "EventHandler"], 73 | ["ArrayBufferLike", "ArrayBufferLike"], 74 | ]); 75 | 76 | export function deepFilter( 77 | obj: T, 78 | fn: (o: any, n: string | undefined) => boolean, 79 | ): T { 80 | if (typeof obj === "object") { 81 | if (Array.isArray(obj)) { 82 | return mapDefined(obj, (e) => 83 | fn(e, undefined) ? deepFilter(e, fn) : undefined, 84 | ) as any as T; 85 | } else { 86 | const result: any = {}; 87 | for (const e in obj) { 88 | if (fn(obj[e], e)) { 89 | result[e] = deepFilter(obj[e], fn); 90 | } 91 | } 92 | return result; 93 | } 94 | } 95 | return obj; 96 | } 97 | 98 | export function filterProperties( 99 | obj: Record, 100 | fn: (o: T) => boolean, 101 | ): Record { 102 | const result: Record = {}; 103 | for (const e in obj) { 104 | if (fn(obj[e])) { 105 | result[e] = obj[e]; 106 | } 107 | } 108 | return result; 109 | } 110 | 111 | export function exposesTo(o: { exposed?: string }, target: string[]): boolean { 112 | if (!o || typeof o.exposed !== "string") { 113 | return true; 114 | } 115 | if (o.exposed === "*") { 116 | return true; 117 | } 118 | return o.exposed.split(" ").some((e) => target.includes(e)); 119 | } 120 | 121 | export function merge(target: T, src: T, shallow?: boolean): T { 122 | if (typeof target !== "object" || typeof src !== "object") { 123 | return src; 124 | } 125 | if (!target || !src) { 126 | throw new Error("Either `target` or `src` is null"); 127 | } 128 | for (const k in src) { 129 | if (Object.getOwnPropertyDescriptor(src, k)) { 130 | if (Object.getOwnPropertyDescriptor(target, k)) { 131 | const targetProp = target[k]; 132 | const srcProp = src[k]; 133 | if (Array.isArray(targetProp) && Array.isArray(srcProp)) { 134 | mergeNamedArrays(targetProp, srcProp); 135 | } else { 136 | if ( 137 | shallow && 138 | typeof (targetProp as any).name === "string" && 139 | typeof (srcProp as any).name === "string" 140 | ) { 141 | target[k] = srcProp; 142 | } else { 143 | if (targetProp === srcProp && k !== "name") { 144 | console.warn( 145 | `Redundant merge value ${targetProp} in ${JSON.stringify(src)}`, 146 | ); 147 | } 148 | target[k] = merge(targetProp, srcProp, shallow); 149 | } 150 | } 151 | } else { 152 | target[k] = src[k]; 153 | } 154 | } 155 | } 156 | return target; 157 | } 158 | 159 | function mergeNamedArrays( 160 | srcProp: T[], 161 | targetProp: T[], 162 | ) { 163 | const map: any = {}; 164 | for (const e1 of srcProp) { 165 | const { name } = e1; 166 | if (name) { 167 | map[name] = e1; 168 | } 169 | } 170 | 171 | for (const e2 of targetProp) { 172 | const { name } = e2; 173 | if (name && map[name]) { 174 | merge(map[name], e2); 175 | } else { 176 | srcProp.push(e2); 177 | } 178 | } 179 | } 180 | 181 | export function distinct(a: T[]): T[] { 182 | return Array.from(new Set(a).values()); 183 | } 184 | 185 | export function mapToArray(m?: Record): T[] { 186 | return Object.keys(m || {}).map((k) => m![k]); 187 | } 188 | 189 | export function arrayToMap( 190 | array: ReadonlyArray, 191 | makeKey: (value: T) => string, 192 | makeValue: (value: T) => U, 193 | ): Record { 194 | const result: Record = {}; 195 | for (const value of array) { 196 | result[makeKey(value)] = makeValue(value); 197 | } 198 | return result; 199 | } 200 | 201 | export function mapValues( 202 | obj: Record | undefined, 203 | fn: (o: T) => U, 204 | ): U[] { 205 | return Object.keys(obj || {}).map((k) => fn(obj![k])); 206 | } 207 | 208 | export function mapDefined( 209 | array: ReadonlyArray | undefined, 210 | mapFn: (x: T, i: number) => U | undefined, 211 | ): U[] { 212 | const result: U[] = []; 213 | if (array) { 214 | for (let i = 0; i < array.length; i++) { 215 | const mapped = mapFn(array[i], i); 216 | if (mapped !== undefined) { 217 | result.push(mapped); 218 | } 219 | } 220 | } 221 | return result; 222 | } 223 | 224 | export function toNameMap( 225 | array: T[], 226 | ): Record { 227 | const result: Record = {}; 228 | for (const value of array) { 229 | result[value.name] = value; 230 | } 231 | return result; 232 | } 233 | 234 | export function concat(a: T[] | undefined, b: T[] | undefined): T[] { 235 | return !a ? b || [] : a.concat(b || []); 236 | } 237 | 238 | export function getEmptyWebIDL(): Browser.WebIdl { 239 | return { 240 | callbackFunctions: { 241 | callbackFunction: {}, 242 | }, 243 | callbackInterfaces: { 244 | interface: {}, 245 | }, 246 | dictionaries: { 247 | dictionary: {}, 248 | }, 249 | enums: { 250 | enum: {}, 251 | }, 252 | interfaces: { 253 | interface: {}, 254 | }, 255 | mixins: { 256 | mixin: {}, 257 | }, 258 | typedefs: { 259 | typedef: [], 260 | }, 261 | namespaces: [], 262 | }; 263 | } 264 | 265 | export function resolveExposure( 266 | obj: Record, 267 | exposure: string, 268 | override?: boolean, 269 | ): void { 270 | if (!exposure) { 271 | throw new Error("No exposure set"); 272 | } 273 | if ("exposed" in obj && (override || obj.exposed === undefined)) { 274 | obj.exposed = exposure; 275 | } 276 | for (const key in obj) { 277 | if (typeof obj[key] === "object" && obj[key]) { 278 | resolveExposure(obj[key], exposure, override); 279 | } 280 | } 281 | } 282 | 283 | function collectTypeReferences(obj: any): string[] { 284 | const collection: string[] = []; 285 | if (typeof obj !== "object") { 286 | return collection; 287 | } 288 | if (Array.isArray(obj)) { 289 | return collection.concat(...obj.map(collectTypeReferences)); 290 | } 291 | 292 | if (typeof obj.type === "string") { 293 | collection.push(obj.type); 294 | } 295 | if (Array.isArray(obj.implements)) { 296 | collection.push(...obj.implements); 297 | } 298 | if (typeof obj.extends === "string") { 299 | collection.push(obj.extends); 300 | } 301 | 302 | for (const e in obj) { 303 | collection.push(...collectTypeReferences(obj[e])); 304 | } 305 | return collection; 306 | } 307 | 308 | function getNonValueTypeMap(webidl: Browser.WebIdl) { 309 | const namedTypes: { name: string }[] = [ 310 | ...mapToArray(webidl.callbackFunctions!.callbackFunction), 311 | ...mapToArray(webidl.callbackInterfaces!.interface), 312 | ...mapToArray(webidl.dictionaries!.dictionary), 313 | ...mapToArray(webidl.enums!.enum), 314 | ...mapToArray(webidl.mixins!.mixin), 315 | ...webidl.typedefs!.typedef, 316 | ]; 317 | return new Map(namedTypes.map((t) => [t.name, t] as [string, any])); 318 | } 319 | 320 | export function followTypeReferences( 321 | webidl: Browser.WebIdl, 322 | filteredInterfaces: Record, 323 | ): Set { 324 | const set = new Set(); 325 | const map = getNonValueTypeMap(webidl); 326 | 327 | new Set(collectTypeReferences(filteredInterfaces)).forEach(follow); 328 | return set; 329 | 330 | function follow(reference: string) { 331 | if ( 332 | baseTypeConversionMap.has(reference) || 333 | reference in filteredInterfaces 334 | ) { 335 | return; 336 | } 337 | const type = map.get(reference); 338 | if (!type) { 339 | return; 340 | } 341 | if (!set.has(type.name)) { 342 | set.add(type.name); 343 | collectTypeReferences(type).forEach(follow); 344 | } 345 | } 346 | } 347 | 348 | export function assertUnique(list: string[]): string[] { 349 | if (new Set(list).size < list.length) { 350 | throw new Error(`Duplicate items found in the list: ${list}`); 351 | } 352 | return list; 353 | } 354 | -------------------------------------------------------------------------------- /src/build/legacy-namespace.ts: -------------------------------------------------------------------------------- 1 | import * as Browser from "./types.js"; 2 | import { mapToArray, arrayToMap } from "./helpers.js"; 3 | 4 | export function collectLegacyNamespaceTypes( 5 | webidl: Browser.WebIdl, 6 | ): Browser.Interface[] { 7 | if (!webidl.namespaces) { 8 | return []; 9 | } 10 | 11 | const namespaceMap: Record = arrayToMap( 12 | webidl.namespaces, 13 | (i) => i.name, 14 | (i) => i, 15 | ); 16 | for (const i of mapToArray(webidl.interfaces!.interface)) { 17 | if (i.legacyNamespace) { 18 | getNamespace(i.legacyNamespace).nested!.interfaces.push(i); 19 | } 20 | } 21 | for (const i of mapToArray(webidl.dictionaries!.dictionary)) { 22 | if (i.legacyNamespace) { 23 | getNamespace(i.legacyNamespace).nested!.dictionaries.push(i); 24 | } 25 | } 26 | for (const i of mapToArray(webidl.enums!.enum)) { 27 | if (i.legacyNamespace) { 28 | getNamespace(i.legacyNamespace).nested!.enums.push(i); 29 | } 30 | } 31 | for (const i of webidl.typedefs!.typedef) { 32 | if (i.legacyNamespace) { 33 | getNamespace(i.legacyNamespace).nested!.typedefs.push(i); 34 | } 35 | } 36 | 37 | return mapToArray(namespaceMap); 38 | 39 | function getNamespace(name: string) { 40 | if (name in namespaceMap) { 41 | const nestedAdded = addEmptyNested(namespaceMap[name]); 42 | namespaceMap[name] = nestedAdded; 43 | return nestedAdded; 44 | } 45 | throw new Error(`Couldn't find a namespace named ${name}.`); 46 | } 47 | } 48 | 49 | function addEmptyNested(namespace: Browser.Interface): Browser.Interface { 50 | if (namespace.nested) { 51 | return namespace; 52 | } 53 | return { 54 | ...namespace, 55 | nested: { 56 | interfaces: [], 57 | enums: [], 58 | dictionaries: [], 59 | typedefs: [], 60 | }, 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /src/build/mdn-comments.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs/promises"; 2 | import { basename } from "path"; 3 | 4 | const basePath = new URL( 5 | "../../inputfiles/mdn/files/en-us/web/api/", 6 | import.meta.url, 7 | ); 8 | 9 | function extractSummary(markdown: string): string { 10 | // Remove frontmatter (--- at the beginning) 11 | markdown = markdown.replace(/^---[\s\S]+?---\n/, ""); 12 | 13 | // Normalize line breaks by collapsing consecutive newlines into a single space 14 | const normalizedText = markdown 15 | .split("\n") 16 | .map((line) => line.trim()) 17 | .filter( 18 | (line) => 19 | line && 20 | !line.startsWith("#") && 21 | !line.startsWith(">") && 22 | !line.startsWith("{{"), 23 | ) 24 | .join(" ") 25 | .replace( 26 | /\{\{\s*(Glossary|HTMLElement|SVGAttr|SVGElement|cssxref|jsxref|HTTPHeader)\s*\(\s*["']((?:\\.|[^"\\])*?)["'].*?\)\s*\}\}/gi, 27 | "$2", 28 | ) // Extract first argument from multiple templates, handling escaped quotes & spaces 29 | .replace( 30 | /\{\{\s*domxref\s*\(\s*["']((?:\\.|[^"\\])*?)["'][^}]*\)\s*\}\}/gi, 31 | "$1", 32 | ) // Extract first argument from domxref, handling spaces 33 | .replace( 34 | /\{\{\s*(?:event|jsxref|cssref|specname)\s*\|\s*([^}]+)\s*\}\}/gi, 35 | "$1", 36 | ) // Handle event, jsxref, cssref, etc. 37 | .replace(/\{\{\s*([^}]+)\s*\}\}/g, (_, match) => `[MISSING: ${match}]`) // Catch any remaining unhandled templates 38 | .replace(/\[(.*?)\]\(.*?\)/g, "$1") // Keep link text but remove URLs 39 | .replace(/\s+/g, " ") // Normalize spaces 40 | .replace(/\n\s*/g, "\n") // Ensure line breaks are preserved 41 | .replace(/"/g, "'") 42 | .trim(); 43 | 44 | // Extract the first sentence (ending in . ! or ?) 45 | const sentenceMatch = normalizedText.match(/(.*?[.!?])(?=\s|$)/); 46 | if (sentenceMatch) { 47 | return sentenceMatch[0]; // Return the first full sentence 48 | } 49 | 50 | return normalizedText.split(" ")[0] || ""; // Fallback: first word if no sentence found 51 | } 52 | 53 | async function getDirectories(dirPath: URL): Promise { 54 | try { 55 | const entries = await fs.readdir(dirPath, { 56 | withFileTypes: true, 57 | }); 58 | return entries 59 | .filter((entry) => entry.isDirectory()) 60 | .map((entry) => new URL(entry.name + "/", dirPath)); 61 | } catch (error) { 62 | console.error("Error reading directories:", error); 63 | return []; 64 | } 65 | } 66 | 67 | async function getIndexMdContents( 68 | folders: URL[], 69 | ): Promise<{ [key: string]: string }> { 70 | const results: { [key: string]: string } = {}; 71 | 72 | for (const folder of folders) { 73 | const indexPath = new URL("index.md", folder); 74 | 75 | try { 76 | const content = await fs.readFile(indexPath, "utf-8"); 77 | 78 | // Improved title extraction 79 | const titleMatch = content.match(/title:\s*["']?([^"'\n]+)["']?/); 80 | const filename = basename(folder.toString()); 81 | const title = titleMatch 82 | ? titleMatch[1].replace(/ extension$/, "") 83 | : filename || ""; 84 | 85 | const summary = extractSummary(content); 86 | results[title] = summary; 87 | } catch (error) { 88 | console.warn(`Skipping ${indexPath}: ${error}`); 89 | } 90 | } 91 | 92 | return results; 93 | } 94 | 95 | export async function generateDescription(): Promise> { 96 | const stats = await fs.stat(basePath); 97 | if (!stats.isDirectory()) { 98 | throw new Error( 99 | "MDN submodule does not exist; try running `git submodule update --init`", 100 | ); 101 | } 102 | try { 103 | const folders = await getDirectories(basePath); 104 | if (folders.length > 0) { 105 | return await getIndexMdContents(folders); 106 | } 107 | } catch (error) { 108 | console.error("Error generating API descriptions:", error); 109 | } 110 | 111 | return {}; 112 | } 113 | -------------------------------------------------------------------------------- /src/build/types.d.ts: -------------------------------------------------------------------------------- 1 | export interface Typed { 2 | type: string | Typed[]; 3 | subtype?: Typed | Typed[]; 4 | nullable?: boolean; 5 | overrideType?: string; 6 | additionalTypes?: string[]; 7 | allowShared?: boolean; 8 | } 9 | 10 | export interface Param extends Typed { 11 | name: string; 12 | optional?: boolean; 13 | variadic?: boolean; 14 | } 15 | 16 | export interface Signature extends Typed { 17 | param?: Param[]; 18 | deprecated?: boolean; 19 | typeParameters?: TypeParameter[]; 20 | } 21 | 22 | export interface Member extends Typed { 23 | name: string; 24 | default?: string; 25 | required?: boolean; 26 | specs?: string; 27 | comment?: string; 28 | } 29 | 30 | export interface Property extends Typed { 31 | name: string; 32 | eventHandler?: string; 33 | readonly?: boolean; 34 | replaceable?: string; 35 | putForwards?: string; 36 | stringifier?: boolean; 37 | tags?: string; 38 | "property-descriptor-not-enumerable"?: string; 39 | "content-attribute"?: string; 40 | "content-attribute-reflects"?: string; 41 | "content-attribute-value-syntax"?: string; 42 | "content-attribute-enum-values"?: string; 43 | "content-attribute-aliases"?: string; 44 | "content-attribute-boolean"?: string; 45 | "css-property"?: string; 46 | "css-property-enum-values"?: string; 47 | "css-property-initial"?: string; 48 | "css-property-value-syntax"?: string; 49 | "css-property-shorthand"?: string; 50 | "css-property-subproperties"?: string; 51 | "css-property-animatable"?: string; 52 | "css-property-aliases"?: string; 53 | "lenient-this"?: string; 54 | "treat-null-as"?: string; 55 | "event-handler-map-to-window"?: string; 56 | static?: boolean; 57 | comment?: string; 58 | optional?: boolean; 59 | specs?: string; 60 | deprecated?: boolean; 61 | exposed?: string; 62 | secureContext?: boolean; 63 | mdnUrl?: string; 64 | } 65 | 66 | export interface Event { 67 | name: string; 68 | type: string; 69 | dispatch?: string; 70 | "skips-window"?: string; 71 | bubbles?: 1; 72 | cancelable?: 1; 73 | follows?: string; 74 | precedes?: string; 75 | tags?: string; 76 | aliases?: string; 77 | specs?: string; 78 | } 79 | 80 | export interface AnonymousMethod { 81 | tags?: string; 82 | static?: boolean; 83 | getter?: boolean; 84 | stringifier?: boolean; 85 | comment?: string; 86 | overrideSignatures?: string[]; 87 | additionalSignatures?: string[]; 88 | specs?: string; 89 | exposed?: string; 90 | deprecated?: boolean; 91 | signature: Signature[]; 92 | secureContext?: boolean; 93 | mdnUrl?: string; 94 | } 95 | 96 | export interface Method extends AnonymousMethod { 97 | name: string; 98 | } 99 | 100 | export interface CallbackFunction { 101 | name: string; 102 | signature: Signature[]; 103 | tags?: string; 104 | overrideSignatures?: string[]; 105 | specs?: string; 106 | typeParameters?: TypeParameter[]; 107 | } 108 | 109 | export interface Constructor { 110 | signature: Signature[]; 111 | comment?: string; 112 | specs?: string; 113 | } 114 | 115 | export interface NamedConstructor { 116 | name: string; 117 | signature: Signature[]; 118 | specs?: string; 119 | } 120 | 121 | export interface Constant extends Typed { 122 | name: string; 123 | value: string; 124 | tags?: string; 125 | exposed?: string; 126 | specs?: string; 127 | comment?: string; 128 | } 129 | 130 | export interface ParsedAttribute { 131 | "enum-values"?: string; 132 | name: string; 133 | "value-syntax"?: string; 134 | } 135 | 136 | export interface Element { 137 | name: string; 138 | namespace?: string; 139 | deprecated?: boolean; 140 | specs?: string; 141 | } 142 | 143 | export interface TypeParameter { 144 | name: string; 145 | extends?: string; 146 | default?: string; 147 | } 148 | 149 | export interface Interface { 150 | name: string; 151 | mixin?: boolean; 152 | namespace?: boolean; 153 | extends?: string; 154 | comment?: string; 155 | constants?: { 156 | constant: Record; 157 | }; 158 | methods: { 159 | method: Record; 160 | }; 161 | events?: { 162 | event: Event[]; 163 | }; 164 | attributelessEvents?: { 165 | event: Event[]; 166 | }; 167 | properties?: { 168 | property: Record; 169 | namesakes?: Record; 170 | }; 171 | constructor?: Constructor; 172 | implements?: string[]; 173 | anonymousMethods?: { 174 | method: AnonymousMethod[]; 175 | }; 176 | "anonymous-content-attributes"?: { 177 | parsedattribute: ParsedAttribute[]; 178 | }; 179 | element?: Element[]; 180 | namedConstructor?: NamedConstructor; 181 | exposed?: string; 182 | overrideExposed?: string; 183 | tags?: string; 184 | "implicit-this"?: 1; 185 | overrideThis?: string; 186 | noInterfaceObject?: boolean; 187 | global?: string; 188 | typeParameters?: TypeParameter[]; 189 | overrideIndexSignatures?: string[]; 190 | specs?: string; 191 | iterator?: Iterator; 192 | legacyWindowAlias?: string[]; 193 | legacyNamespace?: string; 194 | nested?: { 195 | interfaces: Interface[]; 196 | enums: Enum[]; 197 | dictionaries: Dictionary[]; 198 | typedefs: TypeDef[]; 199 | }; 200 | deprecated?: boolean | string; 201 | secureContext?: boolean; 202 | mdnUrl?: string; 203 | transferable?: boolean; 204 | } 205 | 206 | export interface Iterator { 207 | kind: "iterable" | "setlike" | "maplike"; 208 | readonly: boolean; 209 | async: boolean; 210 | type: Typed[]; 211 | param?: Param[]; 212 | comments?: { 213 | comment: Record; 214 | }; 215 | exposed?: string; 216 | deprecated?: boolean | string; 217 | } 218 | 219 | export interface Enum { 220 | name: string; 221 | value: string[]; 222 | specs?: string; 223 | legacyNamespace?: string; 224 | } 225 | 226 | export interface TypeDef extends Typed { 227 | name: string; 228 | deprecated?: boolean; 229 | legacyNamespace?: string; 230 | typeParameters?: TypeParameter[]; 231 | } 232 | 233 | export interface Dictionary { 234 | name: string; 235 | extends?: string; 236 | members: { 237 | member: Record; 238 | }; 239 | overrideIndexSignatures?: string[]; 240 | specs?: string; 241 | typeParameters?: TypeParameter[]; 242 | legacyNamespace?: string; 243 | } 244 | 245 | export interface WebIdl { 246 | callbackFunctions?: { 247 | callbackFunction: Record; 248 | }; 249 | callbackInterfaces?: { 250 | interface: Record; 251 | }; 252 | dictionaries?: { 253 | dictionary: Record; 254 | }; 255 | enums?: { 256 | enum: Record; 257 | }; 258 | interfaces?: { 259 | interface: Record; 260 | }; 261 | mixins?: { 262 | mixin: Record; 263 | }; 264 | typedefs?: { 265 | typedef: TypeDef[]; 266 | }; 267 | namespaces?: Interface[]; 268 | events?: Map>; 269 | } 270 | -------------------------------------------------------------------------------- /src/build/utils/css.ts: -------------------------------------------------------------------------------- 1 | export function hyphenToCamelCase(name: string): string { 2 | const camel = name 3 | .replace(/^-(\w)/, (_, c) => c) 4 | .replace(/-(\w)/g, (_, c) => c.toUpperCase()); 5 | return camel === "float" ? "_float" : camel; 6 | } 7 | 8 | export function camelToHyphenCase(name: string): string { 9 | const dashPrefix = name.startsWith("webkit") ? "-" : ""; 10 | return dashPrefix + name.replace(/[A-Z]/g, (c) => "-" + c.toLowerCase()); 11 | } 12 | -------------------------------------------------------------------------------- /src/build/utils/fs.ts: -------------------------------------------------------------------------------- 1 | import { readFile } from "fs/promises"; 2 | 3 | export async function tryReadFile( 4 | path: string | URL, 5 | ): Promise { 6 | try { 7 | return await readFile(path, "utf-8"); 8 | } catch (err: any) { 9 | if (err.code !== "ENOENT") { 10 | throw err; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/build/utils/map.ts: -------------------------------------------------------------------------------- 1 | export function addToArrayMap( 2 | map: Map, 3 | name: string, 4 | value: T, 5 | ): void { 6 | const array = map.get(name) || []; 7 | array.push(value); 8 | map.set(name, array); 9 | } 10 | 11 | export function addToStringMap( 12 | map: Map, 13 | name: string, 14 | value: string, 15 | ): void { 16 | const old = map.get(name) || ""; 17 | map.set(name, `${old}\n${value}\n`); 18 | } 19 | 20 | export function addToNestedMap( 21 | map: Map>, 22 | name: string, 23 | key: string, 24 | value: string, 25 | ): void { 26 | const nested = map.get(name) ?? new Map(); 27 | nested.set(key, value); 28 | map.set(name, nested); 29 | } 30 | -------------------------------------------------------------------------------- /src/build/utils/record.ts: -------------------------------------------------------------------------------- 1 | export function filterMapRecord( 2 | object: Record | undefined, 3 | mapper: (key: string, value: T) => V | undefined, 4 | forNamespace?: boolean, 5 | ): Record | undefined { 6 | if (!object) { 7 | return; 8 | } 9 | const result = {} as Record; 10 | for (const [key, value] of Object.entries(object)) { 11 | const mdnKey = 12 | forNamespace || ("static" in value && value.static) 13 | ? `${key}_static` 14 | : key; 15 | const newValue = mapper(mdnKey, value); 16 | if (newValue !== undefined) { 17 | result[key] = newValue; 18 | } 19 | } 20 | return result; 21 | } 22 | 23 | export function isEmptyRecord(o: object | undefined): boolean { 24 | return !o || !Object.keys(o).length; 25 | } 26 | -------------------------------------------------------------------------------- /src/build/webref/css.ts: -------------------------------------------------------------------------------- 1 | function hyphenToCamelCase(name: string) { 2 | const camel = name 3 | .replace(/^-(\w)/, (_, c) => c) 4 | .replace(/-(\w)/g, (_, c) => c.toUpperCase()); 5 | return camel === "float" ? "_float" : camel; 6 | } 7 | 8 | export function generateWebIdlFromCssProperties(properties: string[]): string { 9 | return `partial interface CSSStyleDeclaration {${properties 10 | .map( 11 | (property) => 12 | `\n [CEReactions] attribute [LegacyNullToEmptyString] CSSOMString ${hyphenToCamelCase( 13 | property, 14 | )};`, 15 | ) 16 | .join("")}\n};`; 17 | } 18 | -------------------------------------------------------------------------------- /src/build/webref/elements.ts: -------------------------------------------------------------------------------- 1 | import { listAll, Element as WebrefElement } from "@webref/elements"; 2 | import { Interface, WebIdl } from "../types.js"; 3 | import { addToArrayMap } from "../utils/map.js"; 4 | 5 | async function getInterfaceToElementMap(): Promise< 6 | Map 7 | > { 8 | const all = await listAll(); 9 | const map = new Map(); 10 | for (const item of Object.values(all)) { 11 | const { elements } = item; 12 | for (const element of elements) { 13 | if (!element.interface) { 14 | continue; 15 | } 16 | addToArrayMap(map, element.interface, element); 17 | } 18 | } 19 | return map; 20 | } 21 | 22 | export async function getInterfaceElementMergeData(): Promise { 23 | const data: WebIdl = { interfaces: { interface: {} } }; 24 | const map = await getInterfaceToElementMap(); 25 | for (const [key, elements] of map) { 26 | const namespace = key.startsWith("SVG") 27 | ? "SVG" 28 | : key.startsWith("MathML") 29 | ? "MathML" 30 | : undefined; 31 | data.interfaces!.interface[key] = { 32 | element: elements.map((el) => ({ 33 | namespace, 34 | name: el.name, 35 | deprecated: el.obsolete, 36 | })), 37 | } as Interface; 38 | } 39 | return data; 40 | } 41 | -------------------------------------------------------------------------------- /src/build/webref/events.ts: -------------------------------------------------------------------------------- 1 | import { listAll } from "@webref/events"; 2 | import { addToNestedMap } from "../utils/map.js"; 3 | 4 | export async function getInterfaceToEventMap(): Promise< 5 | Map> 6 | > { 7 | const all = await listAll(); 8 | const map = new Map>(); 9 | for (const item of Object.values(all)) { 10 | const { targets } = item; 11 | for (const target of targets) { 12 | addToNestedMap(map, target.target, item.type, item.interface); 13 | for (const path of target.bubblingPath ?? []) { 14 | addToNestedMap(map, path, item.type, item.interface); 15 | } 16 | } 17 | } 18 | return map; 19 | } 20 | -------------------------------------------------------------------------------- /src/build/webref/idl.ts: -------------------------------------------------------------------------------- 1 | import { listAll as listAllIdl } from "@webref/idl"; 2 | import { listAll as listAllCss } from "@webref/css"; 3 | import { generateWebIdlFromCssProperties } from "./css.js"; 4 | import { addToStringMap } from "../utils/map.js"; 5 | 6 | export async function getWebidls(): Promise> { 7 | const idl = await listAllIdl(); 8 | const css = await listAllCss(); 9 | 10 | const map = new Map(); 11 | for (const [key, file] of Object.entries(idl)) { 12 | const text = await file.text(); 13 | map.set(key, text); 14 | } 15 | for (const [key, data] of Object.entries(css)) { 16 | const properties = data.properties.map((p) => p.name); 17 | if (properties.length) { 18 | addToStringMap(map, key, generateWebIdlFromCssProperties(properties)); 19 | } 20 | } 21 | return map; 22 | } 23 | -------------------------------------------------------------------------------- /src/build/webref/webref-css.d.ts: -------------------------------------------------------------------------------- 1 | declare module "@webref/css" { 2 | interface Property { 3 | name: string; 4 | } 5 | interface Data { 6 | properties: Property[]; 7 | } 8 | function listAll(): Promise>; 9 | } 10 | -------------------------------------------------------------------------------- /src/build/webref/webref-elements.d.ts: -------------------------------------------------------------------------------- 1 | declare module "@webref/elements" { 2 | interface Spec { 3 | title: string; 4 | url: string; 5 | } 6 | interface Element { 7 | name: string; 8 | interface?: string; 9 | obsolete?: true; 10 | } 11 | interface Item { 12 | spec: Spec; 13 | elements: Element[]; 14 | } 15 | function listAll(): Promise>; 16 | } 17 | -------------------------------------------------------------------------------- /src/build/webref/webref-events.d.ts: -------------------------------------------------------------------------------- 1 | declare module "@webref/events" { 2 | interface Src { 3 | format: string; 4 | href: string; 5 | } 6 | interface Target { 7 | target: string; 8 | bubbles?: boolean; 9 | bubblingPath?: string[]; 10 | } 11 | interface Item { 12 | src: Src; 13 | href: string; 14 | type: string; 15 | targets: Target[]; 16 | interface: string; 17 | } 18 | function listAll(): Promise>; 19 | } 20 | -------------------------------------------------------------------------------- /src/build/webref/webref-idl.d.ts: -------------------------------------------------------------------------------- 1 | declare module "@webref/idl" { 2 | interface IDLFile { 3 | text(): Promise; 4 | } 5 | function listAll(): Promise>; 6 | } 7 | -------------------------------------------------------------------------------- /src/changelog.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from "child_process"; 2 | import ts from "typescript"; 3 | import { fileURLToPath } from "url"; 4 | 5 | export function gitShowFile(commit: string, path: string): string { 6 | return execSync(`git show ${commit}:${path}`, { encoding: "utf-8" }); 7 | } 8 | 9 | function gitLatestTag() { 10 | return execSync(`git describe --tags --abbrev=0`, { 11 | encoding: "utf-8", 12 | }).trim(); 13 | } 14 | 15 | function mapInterfaceToMembers(interfaces: ts.InterfaceDeclaration[]) { 16 | const interfaceToMemberMap = new Map(); 17 | for (const decl of interfaces) { 18 | interfaceToMemberMap.set( 19 | decl.name.text, 20 | decl.members.map((m) => m.name?.getText()).filter((n) => n) as string[], 21 | ); 22 | } 23 | return interfaceToMemberMap; 24 | } 25 | 26 | function extractTypesFromFile(file: string) { 27 | const source = ts.createSourceFile( 28 | "dom", 29 | file, 30 | ts.ScriptTarget.ES2015, 31 | /*setParentNodes */ true, 32 | ); 33 | 34 | const interfaceNames = source.statements 35 | .filter(ts.isVariableStatement) 36 | .map((v) => v.declarationList.declarations[0].name.getText(source)); 37 | const tsInterfacedecls = source.statements.filter(ts.isInterfaceDeclaration); 38 | const idlInterfaceDecls = tsInterfacedecls.filter((i) => 39 | interfaceNames.includes(i.name.text), 40 | ); 41 | const otherDecls = tsInterfacedecls.filter( 42 | (i) => !interfaceNames.includes(i.name.text), 43 | ); 44 | 45 | const interfaceToMemberMap = mapInterfaceToMembers(idlInterfaceDecls); 46 | const otherToMemberMap = mapInterfaceToMembers(otherDecls); 47 | 48 | return { interfaceToMemberMap, otherToMemberMap }; 49 | } 50 | 51 | function compareSet(x: Set, y: Set) { 52 | function intersection(x: Set, y: Set) { 53 | const result = new Set(); 54 | for (const i of y) { 55 | if (x.has(i)) { 56 | result.add(i); 57 | } 58 | } 59 | return result; 60 | } 61 | function difference(x: Set, y: Set) { 62 | const result = new Set(x); 63 | for (const i of y) { 64 | result.delete(i); 65 | } 66 | return result; 67 | } 68 | const common = intersection(x, y); 69 | const added = difference(y, common); 70 | const removed = difference(x, common); 71 | return { added, removed, common }; 72 | } 73 | 74 | function diffTypes(previous: string, current: string) { 75 | function diff( 76 | previousMap: Map, 77 | currentMap: Map, 78 | ) { 79 | const { added, removed, common } = compareSet( 80 | new Set(previousMap.keys()), 81 | new Set(currentMap.keys()), 82 | ); 83 | const modified = new Map< 84 | string, 85 | { added: Set; removed: Set } 86 | >(); 87 | for (const name of common) { 88 | const previousMembers = new Set(previousMap.get(name)); 89 | const currentMembers = new Set(currentMap.get(name)); 90 | const { added, removed } = compareSet(previousMembers, currentMembers); 91 | if (!added.size && !removed.size) { 92 | continue; 93 | } 94 | modified.set(name, { added, removed }); 95 | } 96 | return { added, removed, modified }; 97 | } 98 | 99 | const previousTypes = extractTypesFromFile(previous); 100 | const currentTypes = extractTypesFromFile(current); 101 | return { 102 | interfaces: diff( 103 | previousTypes.interfaceToMemberMap, 104 | currentTypes.interfaceToMemberMap, 105 | ), 106 | others: diff(previousTypes.otherToMemberMap, currentTypes.otherToMemberMap), 107 | }; 108 | } 109 | 110 | function writeAddedRemoved(added: Set, removed: Set) { 111 | function newlineSeparatedList(names: Set) { 112 | return [...names].map((a) => `* \`${a}\``).join("\n"); 113 | } 114 | const output = []; 115 | if (added.size) { 116 | output.push(`## New interfaces\n\n${newlineSeparatedList(added)}`); 117 | } 118 | if (removed.size) { 119 | output.push(`## Removed interfaces\n\n${newlineSeparatedList(removed)}`); 120 | } 121 | return output.join("\n\n"); 122 | } 123 | 124 | function writeAddedRemovedInline(added: Set, removed: Set) { 125 | function commaSeparatedList(names: Set) { 126 | return [...names].map((a) => `\`${a}\``).join(", "); 127 | } 128 | const output = []; 129 | if (added.size) { 130 | output.push(` * Added: ${commaSeparatedList(added)}`); 131 | } 132 | if (removed.size) { 133 | output.push(` * Removed: ${commaSeparatedList(removed)}`); 134 | } 135 | return output.join("\n"); 136 | } 137 | 138 | const dom = "baselines/dom.generated.d.ts"; 139 | 140 | export function generateDefaultFromRecentTag(): string { 141 | const [base = gitLatestTag(), head = "HEAD"] = process.argv.slice(2); 142 | const previous = gitShowFile(base, dom); 143 | const current = gitShowFile(head, dom); 144 | const changelog = generateChangelogFrom(previous, current); 145 | if (!changelog.length) { 146 | throw new Error(`No change reported between ${base} and ${head}.`); 147 | } 148 | return changelog; 149 | } 150 | 151 | export function generateChangelogFrom( 152 | previous: string, 153 | current: string, 154 | ): string { 155 | const { 156 | interfaces: { added, removed, modified }, 157 | others, 158 | } = diffTypes(previous, current); 159 | 160 | const outputs = []; 161 | if (added.size || removed.size) { 162 | outputs.push(writeAddedRemoved(added, removed)); 163 | } 164 | 165 | if (modified.size) { 166 | const modifiedOutput = [`## Modified\n`]; 167 | for (const [key, value] of modified.entries()) { 168 | modifiedOutput.push(`* ${key}`); 169 | modifiedOutput.push(writeAddedRemovedInline(value.added, value.removed)); 170 | } 171 | outputs.push(modifiedOutput.join("\n")); 172 | } 173 | 174 | if (others.modified.size) { 175 | const modifiedOutput = [`### Non-value types\n`]; 176 | for (const [key, value] of others.modified.entries()) { 177 | modifiedOutput.push(`* ${key}`); 178 | modifiedOutput.push(writeAddedRemovedInline(value.added, value.removed)); 179 | } 180 | outputs.push(modifiedOutput.join("\n")); 181 | } 182 | 183 | const output = outputs.join("\n\n"); 184 | return output; 185 | } 186 | 187 | if (process.argv[1] === fileURLToPath(import.meta.url)) { 188 | console.log(generateDefaultFromRecentTag()); 189 | } 190 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | import child_process from "child_process"; 3 | import { printUnifiedDiff } from "print-diff"; 4 | import { fileURLToPath } from "url"; 5 | 6 | const baselineFolder = new URL("../baselines/", import.meta.url); 7 | const outputFolder = new URL("../generated/", import.meta.url); 8 | const tscPath = new URL( 9 | "../node_modules/typescript/lib/tsc.js", 10 | import.meta.url, 11 | ); 12 | 13 | function normalizeLineEndings(text: string): string { 14 | return text.replace(/\r\n?/g, "\n"); 15 | } 16 | 17 | function compareToBaselines(baselineFolder: URL, outputFolder: URL) { 18 | let baselineFiles: string[] = []; 19 | try { 20 | baselineFiles = fs.readdirSync(baselineFolder); 21 | } catch { 22 | // do nothing 23 | } 24 | 25 | let outputFiles: string[] = []; 26 | try { 27 | outputFiles = fs.readdirSync(outputFolder); 28 | } catch { 29 | // do nothing 30 | } 31 | 32 | for (const file of new Set([...baselineFiles, ...outputFiles])) { 33 | if (file.startsWith(".")) { 34 | continue; 35 | } 36 | 37 | let baselineStats: fs.Stats | undefined; 38 | try { 39 | baselineStats = fs.statSync(new URL(file, baselineFolder)); 40 | } catch { 41 | // do nothing 42 | } 43 | 44 | let outputStats: fs.Stats | undefined; 45 | try { 46 | outputStats = fs.statSync(new URL(file, outputFolder)); 47 | } catch { 48 | // do nothing 49 | } 50 | 51 | const baseline = baselineStats?.isFile() 52 | ? normalizeLineEndings( 53 | fs.readFileSync(new URL(file, baselineFolder)).toString(), 54 | ) 55 | : null; 56 | 57 | const generated = outputStats?.isFile() 58 | ? normalizeLineEndings( 59 | fs.readFileSync(new URL(file, outputFolder)).toString(), 60 | ) 61 | : null; 62 | 63 | if (baseline !== null || generated !== null) { 64 | if (baseline !== generated) { 65 | console.error( 66 | `Test failed: '${file}' is different from baseline file.`, 67 | ); 68 | printUnifiedDiff(baseline ?? "", generated ?? ""); 69 | return false; 70 | } 71 | 72 | continue; 73 | } 74 | 75 | if (baselineStats?.isDirectory() || outputStats?.isDirectory()) { 76 | const childBaselineFolder = new URL(`${file}/`, baselineFolder); 77 | const childOutputFolder = new URL(`${file}/`, outputFolder); 78 | if (!compareToBaselines(childBaselineFolder, childOutputFolder)) { 79 | return false; 80 | } 81 | 82 | continue; 83 | } 84 | } 85 | return true; 86 | } 87 | 88 | function compileGeneratedFiles(lib: string, ...files: string[]) { 89 | try { 90 | child_process.execSync( 91 | `node ${fileURLToPath( 92 | tscPath, 93 | )} --strict --lib ${lib} --types --noEmit ${files 94 | .map((file) => fileURLToPath(new URL(file, outputFolder))) 95 | .join(" ")}`, 96 | ); 97 | } catch (e: any) { 98 | console.error(`Test failed: could not compile '${files.join(",")}':`); 99 | console.error(e.stdout.toString()); 100 | console.error(); 101 | return false; 102 | } 103 | return true; 104 | } 105 | 106 | function test() { 107 | const targets = ["es5", "es6", "es2018"]; 108 | const modules = [ 109 | "dom", 110 | "webworker", 111 | "sharedworker", 112 | "serviceworker", 113 | "audioworklet", 114 | ]; 115 | const suffixes: Record = { 116 | es5: ["generated.d.ts"], 117 | es6: ["generated.d.ts", "iterable.generated.d.ts"], 118 | es2018: ["generated.d.ts", "asynciterable.generated.d.ts"], 119 | }; 120 | 121 | const allPassed = 122 | compareToBaselines(baselineFolder, outputFolder) && 123 | modules.every((mod) => 124 | targets.every((target) => 125 | compileGeneratedFiles( 126 | target, 127 | ...suffixes[target].map((suffix) => `${mod}.${suffix}`), 128 | ), 129 | ), 130 | ); 131 | 132 | if (allPassed) { 133 | console.log("All tests passed."); 134 | process.exit(0); 135 | } 136 | 137 | process.exit(1); 138 | } 139 | 140 | test(); 141 | -------------------------------------------------------------------------------- /src/version.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from "child_process"; 2 | import { readFile, writeFile } from "fs/promises"; 3 | import { generateDefaultFromRecentTag } from "./changelog.js"; 4 | 5 | const output = generateDefaultFromRecentTag(); 6 | 7 | const path = new URL("../CHANGELOG.md", import.meta.url); 8 | 9 | const content = await readFile(path, "utf-8"); 10 | 11 | const { npm_package_version } = process.env; 12 | 13 | await writeFile(path, `# v${npm_package_version}\n\n${output}\n\n${content}`); 14 | 15 | execSync("git add CHANGELOG.md"); 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "outDir": "./lib", 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "sourceMap": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "resolveJsonModule": true 13 | }, 14 | "include": [ 15 | "./src" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /unittests/files/audioworklet/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "lib": ["es2020"], 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "sourceMap": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true 12 | }, 13 | "include": [ 14 | "../../../baselines/audioworklet*", 15 | "**/*" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /unittests/files/audioworklet/usage.ts: -------------------------------------------------------------------------------- 1 | registerProcessor( 2 | "test", 3 | class extends AudioWorkletProcessor { 4 | process(input: Float32Array[][], output: Float32Array[][], params: { [key: string]: Float32Array }) { 5 | return true; 6 | } 7 | } 8 | ); 9 | -------------------------------------------------------------------------------- /unittests/files/autocomplete.ts: -------------------------------------------------------------------------------- 1 | // @ts-expect-error should be a string 2 | document.body.getElementsByTagName("input")[0].autocomplete = false; 3 | // @ts-expect-error wrong value for this attribute 4 | document.body.getElementsByTagName("input")[0].autocomplete = "undefined"; 5 | // @ts-expect-error does not accept boolean attributes 6 | document.body.getElementsByTagName("input")[0].autocomplete = "true"; 7 | // @ts-expect-error does not accept boolean attributes 8 | document.body.getElementsByTagName("input")[0].autocomplete = "false"; 9 | 10 | // @ts-expect-error missing autofill token before webauthn 11 | document.body.getElementsByTagName("input")[0].autocomplete = "webauthn"; 12 | 13 | // @ts-expect-error wrong order for webauthn token 14 | document.body.getElementsByTagName("input")[0].autocomplete = 15 | "webauthn username"; 16 | 17 | // @ts-expect-error wrong order for contact specifier 18 | document.body.getElementsByTagName("input")[0].autocomplete = "tel mobile"; 19 | 20 | // @ts-expect-error off should be standalone 21 | document.body.getElementsByTagName("input")[0].autocomplete = 22 | "section-wrong off"; 23 | 24 | // @ts-expect-error on should be standalone 25 | document.body.getElementsByTagName("input")[0].autocomplete = "on username"; 26 | 27 | // @ts-expect-error home, work or mobile are only for contact tokens 28 | document.body.getElementsByTagName("input")[0].autocomplete = "mobile username"; 29 | 30 | // @ts-expect-error should probably be current-password or new-password 31 | document.body.getElementsByTagName("input")[0].autocomplete = "password"; 32 | 33 | document.body.getElementsByTagName("input")[0].autocomplete = ""; 34 | document.body.getElementsByTagName("input")[0].autocomplete = "on"; 35 | document.body.getElementsByTagName("input")[0].autocomplete = "off"; 36 | document.body.getElementsByTagName("input")[0].autocomplete = "new-password"; 37 | document.body.getElementsByTagName("input")[0].autocomplete = 38 | "current-password"; 39 | document.body.getElementsByTagName("input")[0].autocomplete = "one-time-code"; 40 | 41 | document.body.getElementsByTagName("input")[0].autocomplete = 42 | "username webauthn"; 43 | 44 | document.body.getElementsByTagName("input")[0].autocomplete = 45 | "shipping street-address"; 46 | document.body.getElementsByTagName("input")[0].autocomplete = 47 | "section-custom shipping street-address"; 48 | 49 | document.body.getElementsByTagName("input")[0].autocomplete = "work email"; 50 | document.body.getElementsByTagName("input")[0].autocomplete = 51 | "section-custom billing mobile tel"; 52 | 53 | // @ts-expect-error only on and off are available on a form 54 | document.body.getElementsByTagName("form")[0].autocomplete = "new-password"; 55 | 56 | document.body.getElementsByTagName("form")[0].autocomplete = "off"; 57 | 58 | // @ts-expect-error autocomplete attribute is only for form elements 59 | document.body.getElementsByTagName("p")[0].autocomplete = "off"; 60 | -------------------------------------------------------------------------------- /unittests/files/childnode.ts: -------------------------------------------------------------------------------- 1 | document.body.childNodes[0].remove(); 2 | -------------------------------------------------------------------------------- /unittests/files/differingaccessors.ts: -------------------------------------------------------------------------------- 1 | document.location.href.padStart(3); 2 | document.location = "abc"; 3 | -------------------------------------------------------------------------------- /unittests/files/eventlistener.ts: -------------------------------------------------------------------------------- 1 | document.addEventListener("arbitrary_invalid_event", (ev) => { 2 | return ev.returnValue; 3 | }); 4 | 5 | document.addEventListener("arbitrary_invalid_event", { 6 | handleEvent(ev) { 7 | return ev.returnValue; 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /unittests/files/eventsource.ts: -------------------------------------------------------------------------------- 1 | const source = new EventSource("/"); 2 | 3 | source.addEventListener("custom", (event) => { 4 | event.data; // MessageEvent specific 5 | }); 6 | source.addEventListener("custom", (event: MessageEvent) => {}); 7 | source.addEventListener("with-handle-event", { handleEvent(event: Event) {} }); 8 | -------------------------------------------------------------------------------- /unittests/files/formData.ts: -------------------------------------------------------------------------------- 1 | const formData = new FormData(); 2 | 3 | // Should work 4 | formData.append("myProp", "value"); 5 | 6 | // Should work 7 | formData.append("myProp", new Blob()); 8 | 9 | // Should work 10 | formData.append("myProp", new Blob(), "myFile"); 11 | 12 | // @ts-expect-error will error on runtime 13 | formData.append("myProp", "value", "myFile"); 14 | 15 | // Should work 16 | formData.set("myProp", "value"); 17 | 18 | // Should work 19 | formData.set("myProp", new Blob()); 20 | 21 | // Should work 22 | formData.set("myProp", new Blob(), "myFile"); 23 | 24 | // @ts-expect-error will error on runtime 25 | formData.set("myProp", "value", "myFile"); 26 | 27 | // Should work for union type 28 | type stringBlobUnion = string | Blob; 29 | const unionValue = "value" as stringBlobUnion; 30 | 31 | formData.set("myProp", unionValue); 32 | formData.append("myProp", unionValue); 33 | -------------------------------------------------------------------------------- /unittests/files/htmldocument.ts: -------------------------------------------------------------------------------- 1 | document instanceof HTMLDocument 2 | -------------------------------------------------------------------------------- /unittests/files/keyusage.ts: -------------------------------------------------------------------------------- 1 | function assertType(_x: T) {} 2 | 3 | const mockKey = {} as CryptoKey; 4 | 5 | assertType>(crypto.subtle.exportKey("jwk", mockKey)); 6 | assertType>(crypto.subtle.exportKey("pkcs8", mockKey)); 7 | assertType>(crypto.subtle.exportKey("raw", mockKey)); 8 | assertType>(crypto.subtle.exportKey("spki", mockKey)); 9 | 10 | assertType>( 11 | crypto.subtle 12 | .exportKey("" as KeyFormat, mockKey) 13 | .then((ambiguousExportedKeyData) => 14 | ambiguousExportedKeyData instanceof ArrayBuffer 15 | ? (ambiguousExportedKeyData satisfies ArrayBuffer) 16 | : (ambiguousExportedKeyData satisfies JsonWebKey) 17 | ) 18 | ); 19 | 20 | const usageInline = crypto.subtle.generateKey( 21 | { 22 | name: "AES-GCM", 23 | length: 256, 24 | }, 25 | true, 26 | ["encrypt", "decrypt"] 27 | ); 28 | 29 | const usageConst = crypto.subtle.generateKey( 30 | { 31 | name: "AES-GCM", 32 | length: 256, 33 | }, 34 | true, 35 | ["encrypt", "decrypt"] as const 36 | ); 37 | 38 | const keyUsage: ReadonlyArray = ["encrypt", "decrypt"]; 39 | const usageAsReadonly = crypto.subtle.generateKey( 40 | { 41 | name: "AES-GCM", 42 | length: 256, 43 | }, 44 | true, 45 | keyUsage 46 | ); 47 | -------------------------------------------------------------------------------- /unittests/files/mathml.ts: -------------------------------------------------------------------------------- 1 | let merror: MathMLElement | null = document.createElementNS( 2 | "http://www.w3.org/1998/Math/MathML", 3 | "merror" 4 | ); 5 | merror = document.querySelector("merror"); 6 | merror = document.querySelectorAll("merror")[0]; 7 | merror = document.body.closest("merror"); 8 | merror = document.getElementsByTagName("merror")[0]; 9 | merror = document.getElementsByTagNameNS( 10 | "http://www.w3.org/1998/Math/MathML", 11 | "merror" 12 | )[0]; 13 | -------------------------------------------------------------------------------- /unittests/files/streams.ts: -------------------------------------------------------------------------------- 1 | function assertType(_x: T) {} 2 | 3 | const readableStream = new ReadableStream({ 4 | start(controller) { 5 | assertType>(controller); 6 | controller.desiredSize; 7 | controller.enqueue("a"); 8 | }, 9 | pull(controller) { 10 | assertType>(controller); 11 | controller.enqueue("b"); 12 | controller.close(); 13 | }, 14 | cancel(reason) { 15 | assertType(reason); 16 | }, 17 | }); 18 | 19 | const defaultReader1 = readableStream.getReader(); 20 | assertType>(defaultReader1); 21 | defaultReader1.read().then((result) => { 22 | assertType>(result); 23 | if (!result.done) { 24 | result.value.charAt(0); 25 | } 26 | }); 27 | 28 | const readableByteStream = new ReadableStream({ 29 | type: "bytes", 30 | start(controller) { 31 | assertType(controller); 32 | controller.desiredSize; 33 | controller.byobRequest; 34 | }, 35 | pull(controller) { 36 | assertType(controller); 37 | if (controller.byobRequest) { 38 | assertType(controller.byobRequest); 39 | controller.byobRequest.view; 40 | controller.byobRequest.respond(1); 41 | controller.byobRequest.respondWithNewView(new Uint32Array(1)); 42 | } else { 43 | controller.enqueue(new Uint8Array(1)); 44 | } 45 | controller.close(); 46 | }, 47 | cancel(reason) { 48 | assertType(reason); 49 | }, 50 | }); 51 | 52 | const defaultReader2 = readableByteStream.getReader(); 53 | assertType>(defaultReader2); 54 | defaultReader2.read().then((result) => { 55 | assertType>(result); 56 | if (!result.done) { 57 | result.value.buffer; 58 | result.value[0]; 59 | } 60 | }); 61 | 62 | const byobReader = readableByteStream.getReader({ mode: "byob" }); 63 | assertType(byobReader); 64 | byobReader.read(new Uint8Array(1)); 65 | byobReader.read(new DataView(new ArrayBuffer(1))).then((result) => { 66 | assertType>(result); 67 | if (!result.done) { 68 | result.value.getUint8(0); 69 | } 70 | }); 71 | 72 | const writableStream = new WritableStream({ 73 | start(controller) { 74 | assertType(controller); 75 | }, 76 | write(chunk, controller) { 77 | assertType(chunk); 78 | assertType(controller); 79 | chunk.toFixed(3); 80 | controller.error("boom!"); 81 | }, 82 | close() {}, 83 | abort(reason) { 84 | assertType(reason); 85 | }, 86 | }); 87 | 88 | const defaultWriter = writableStream.getWriter(); 89 | assertType>(defaultWriter); 90 | defaultWriter.write(42); 91 | defaultWriter.close(); 92 | defaultWriter.abort("boom!"); 93 | 94 | const transformStream = new TransformStream({ 95 | start(controller) { 96 | assertType>(controller); 97 | }, 98 | transform(chunk, controller) { 99 | assertType(chunk); 100 | assertType>(controller); 101 | controller.enqueue(chunk.length); 102 | controller.error("boom!"); 103 | }, 104 | flush(controller) { 105 | assertType>(controller); 106 | controller.terminate(); 107 | }, 108 | }); 109 | assertType>(transformStream.readable); 110 | assertType>(transformStream.writable); 111 | -------------------------------------------------------------------------------- /unittests/files/structuredClone.ts: -------------------------------------------------------------------------------- 1 | function assertType(_x: T) {} 2 | 3 | const toBeCloned = { 4 | name: "abc", 5 | address: "test", 6 | age: 30, 7 | info: { 8 | url: "https://example.com", 9 | }, 10 | } as const; 11 | 12 | const nonMatchingType = { foo: "bar" } as const; 13 | const clone = structuredClone(toBeCloned); 14 | 15 | assertType(clone); 16 | // @ts-expect-error non matching type 17 | assertType(clone); 18 | -------------------------------------------------------------------------------- /unittests/files/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "lib": ["es2020"], 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "sourceMap": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true 12 | }, 13 | "include": [ 14 | "../../baselines/dom*", 15 | "**/*" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /unittests/files/url.ts: -------------------------------------------------------------------------------- 1 | export const url = new URL("https://example.com"); 2 | 3 | new EventSource(url); 4 | location.assign(url); 5 | navigator.sendBeacon(url); 6 | navigator.registerProtocolHandler("x-test", url); 7 | new Worker(url); 8 | new SharedWorker(url); 9 | window.open(url); 10 | Response.redirect(url); 11 | -------------------------------------------------------------------------------- /unittests/index.js: -------------------------------------------------------------------------------- 1 | import { execSync } from "child_process"; 2 | import { readdir } from "fs/promises"; 3 | 4 | for (const filename of await readdir(new URL("files/", import.meta.url))) { 5 | if (filename.endsWith(".ts")) { 6 | execSync(`npx tsc generated/dom.generated.d.ts unittests/files/${filename} --target es2020 --lib es2020 --types --noEmit`, { 7 | stdio: "inherit" 8 | }); 9 | } 10 | } 11 | 12 | for (const filename of await readdir(new URL("files/audioworklet", import.meta.url))) { 13 | if (filename.endsWith(".ts")) { 14 | execSync(`npx tsc generated/audioworklet.generated.d.ts unittests/files/audioworklet/${filename} --target es2020 --lib es2020 --types --noEmit`, { 15 | stdio: "inherit" 16 | }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /unittests/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "esModuleInterop": true, 8 | "sourceMap": true, 9 | "noUnusedLocals": true, 10 | "noUnusedParameters": true 11 | }, 12 | "include": [ 13 | "index.js" 14 | ] 15 | } 16 | --------------------------------------------------------------------------------