diff --git a/.editorconfig b/.editorconfig index c9b43cb1..ac1776d0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,6 +14,7 @@ file_header_template = Copyright (c) David Pine. All rights reserved.\nLicensed indent_size = 4 tab_width = 4 trim_trailing_whitespace = true +end_of_line = lf ############################### # .NET Coding Conventions # diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 429e2997..a27207d5 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -9,5 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username -lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/workflows/build-validation.yml b/.github/workflows/build-validation.yml index 61c3c9e6..27b8acec 100644 --- a/.github/workflows/build-validation.yml +++ b/.github/workflows/build-validation.yml @@ -38,10 +38,10 @@ jobs: if: ${{ github.event_name == 'workflow_dispatch' }} run: | echo 'Reason: ${{ github.event.inputs.reason }}' - - name: Setup .NET 7.0 + - name: Setup .NET uses: actions/setup-dotnet@main with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Restore dependencies for ${{ matrix.project }} run: | @@ -59,10 +59,10 @@ jobs: steps: - uses: actions/checkout@main - - name: Setup .NET 7.0 + - name: Setup .NET uses: actions/setup-dotnet@main with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Run tests run: | @@ -74,7 +74,7 @@ jobs: - name: Install Playwright dependencies run: | - pwsh ./tests/Blazor.ExampleConsumer.EndToEndTests/bin/Release/net7.0/playwright.ps1 install --with-deps + pwsh ./tests/Blazor.ExampleConsumer.EndToEndTests/bin/Release/net8.0/playwright.ps1 install --with-deps - name: Run end-to-end tests run: | diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml index 123224c8..d5b414d7 100644 --- a/.github/workflows/publish-nuget.yml +++ b/.github/workflows/publish-nuget.yml @@ -12,7 +12,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@main with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Test run: dotnet test --filter "Category!=EndToEnd" --configuration Release @@ -22,7 +22,7 @@ jobs: - name: Install Playwright dependencies run: | - pwsh ./tests/Blazor.ExampleConsumer.EndToEndTests/bin/Release/net7.0/playwright.ps1 install --with-deps + pwsh ./tests/Blazor.ExampleConsumer.EndToEndTests/bin/Release/net8.0/playwright.ps1 install --with-deps - name: Run end-to-end tests run: | @@ -65,11 +65,11 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@main with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Restore dependencies for ${{ matrix.project }} run: | - dotnet restore ./src/${{ matrix.project }}/${{ matrix.project }}.csproj --configuration Release + dotnet restore ./src/${{ matrix.project }}/${{ matrix.project }}.csproj - name: Build ${{ matrix.project }} run: | diff --git a/.github/workflows/publish-to-gh-pages.yml b/.github/workflows/publish-to-gh-pages.yml index bac5ab3d..cc9eaa31 100644 --- a/.github/workflows/publish-to-gh-pages.yml +++ b/.github/workflows/publish-to-gh-pages.yml @@ -1,47 +1,47 @@ -name: publish demo -on: - push: - branches: [ main ] +name: publish demo +on: + push: + branches: [ main ] paths: - - 'samples/**' + - 'samples/**' workflow_dispatch: inputs: reason: description: 'The reason for running the workflow' required: true - default: 'Manual run' -jobs: - deploy-to-github-pages: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@main + default: 'Manual run' +jobs: + deploy-to-github-pages: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@main - name: 'Print manual run reason' if: ${{ github.event_name == 'workflow_dispatch' }} run: | - echo 'Reason: ${{ github.event.inputs.reason }}' - - name: Setup .NET Core SDK - uses: actions/setup-dotnet@main - with: - dotnet-version: 7.0.x - - name: Publish .NET Core Project - run: dotnet publish samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj -c Release -o release --nologo - - # Changes the base-tag in index.html from '/' to '/blazorators' to match GitHub Pages repository subdirectory - - name: Change base-tag in index.html from / to /blazorators - run: sed -i 's///g' release/wwwroot/index.html - - # Copy index.html to 404.html to serve the same file when a file is not found - - name: copy index.html to 404.html - run: cp release/wwwroot/index.html release/wwwroot/404.html - - # Add .nojekyll file to tell GitHub pages to not treat this as a Jekyll project. - # Allow files and folders starting with an underscore - - name: Add .nojekyll file - run: touch release/wwwroot/.nojekyll - - - name: Commit wwwroot to GitHub Pages - uses: JamesIves/github-pages-deploy-action@3.7.1 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BRANCH: gh-pages + echo 'Reason: ${{ github.event.inputs.reason }}' + - name: Setup .NET + uses: actions/setup-dotnet@main + with: + dotnet-version: 8.0.x + - name: Publish .NET Core Project + run: dotnet publish samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj -c Release -o release --nologo + + # Changes the base-tag in index.html from '/' to '/blazorators' to match GitHub Pages repository subdirectory + - name: Change base-tag in index.html from / to /blazorators + run: sed -i 's///g' release/wwwroot/index.html + + # Copy index.html to 404.html to serve the same file when a file is not found + - name: copy index.html to 404.html + run: cp release/wwwroot/index.html release/wwwroot/404.html + + # Add .nojekyll file to tell GitHub pages to not treat this as a Jekyll project. + # Allow files and folders starting with an underscore + - name: Add .nojekyll file + run: touch release/wwwroot/.nojekyll + + - name: Commit wwwroot to GitHub Pages + uses: JamesIves/github-pages-deploy-action@3.7.1 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: gh-pages FOLDER: release/wwwroot \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index e1c28a5c..ab82f05d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -12,7 +12,8 @@ - net7.0 + net7.0;net8.0 + preview diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 00000000..54f53b5a --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,38 @@ + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blazorators.sln b/blazorators.sln index f0f57133..e706b853 100644 --- a/blazorators.sln +++ b/blazorators.sln @@ -12,7 +12,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig .gitignore = .gitignore Directory.Build.props = Directory.Build.props - global.json = global.json LICENSE = LICENSE logo-large.png = logo-large.png logo.png = logo.png diff --git a/global.json b/global.json deleted file mode 100644 index 531a365b..00000000 --- a/global.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "sdk": { - "version": "7.0.110", - "rollForward": "latestPatch" - } -} \ No newline at end of file diff --git a/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj b/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj index 8d9b94f9..557a0956 100644 --- a/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj +++ b/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj @@ -1,24 +1,24 @@  - - net7.0 - enable - enable - + + net8.0 + enable + enable + - - - - - + + + + + - - - - - - - - + + + + + + + + diff --git a/samples/Blazor.ExampleConsumer/App.razor b/samples/Blazor.ExampleConsumer/Components/App.razor similarity index 100% rename from samples/Blazor.ExampleConsumer/App.razor rename to samples/Blazor.ExampleConsumer/Components/App.razor diff --git a/samples/Blazor.ExampleConsumer/Components/Layout/MainLayout.razor b/samples/Blazor.ExampleConsumer/Components/Layout/MainLayout.razor new file mode 100644 index 00000000..ba059844 --- /dev/null +++ b/samples/Blazor.ExampleConsumer/Components/Layout/MainLayout.razor @@ -0,0 +1,19 @@ +@using System.Runtime.InteropServices +@inherits LayoutComponentBase + +
+ + +
+
+ Powered by @RuntimeInformation.FrameworkDescription +
+ +
+ @Body +
+ +
+
diff --git a/samples/Blazor.ExampleConsumer/Shared/MainLayout.razor.css b/samples/Blazor.ExampleConsumer/Components/Layout/MainLayout.razor.css similarity index 90% rename from samples/Blazor.ExampleConsumer/Shared/MainLayout.razor.css rename to samples/Blazor.ExampleConsumer/Components/Layout/MainLayout.razor.css index c7ac7630..e4745a4c 100644 --- a/samples/Blazor.ExampleConsumer/Shared/MainLayout.razor.css +++ b/samples/Blazor.ExampleConsumer/Components/Layout/MainLayout.razor.css @@ -1,81 +1,79 @@ -.page { - position: relative; - display: flex; - flex-direction: column; -} - -main { - flex: 1; -} - -.sidebar { - background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); -} - -.top-row { - background-color: #f7f7f7; - border-bottom: 1px solid #d6d5d5; - justify-content: flex-end; - height: 3.5rem; - display: flex; - align-items: center; -} - - .top-row ::deep a, .top-row ::deep .btn-link { - white-space: nowrap; - margin-left: 1.5rem; - text-decoration: none; - } - - .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { - text-decoration: underline; - } - - .top-row ::deep a:first-child { - overflow: hidden; - text-overflow: ellipsis; - } - -@media (max-width: 640.98px) { - .top-row:not(.auth) { - display: none; - } - - .top-row.auth { - justify-content: space-between; - } - - .top-row ::deep a, .top-row ::deep .btn-link { - margin-left: 0; - } -} - -@media (min-width: 641px) { - .page { - flex-direction: row; - } - - .sidebar { - width: 250px; - height: 100vh; - position: sticky; - top: 0; - } - - .top-row { - position: sticky; - top: 0; - z-index: 1; - } - - .top-row.auth ::deep a:first-child { - flex: 1; - text-align: right; - width: 0; - } - - .top-row, article { - padding-left: 2rem !important; - padding-right: 1.5rem !important; - } -} +.page { + position: relative; + display: flex; + flex-direction: column; +} + +main { + flex: 1; +} + +.sidebar { + background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); +} + +.top-row { + background-color: #f7f7f7; + border-bottom: 1px solid #d6d5d5; + height: 3.5rem; + display: flex; + align-items: center; +} + + .top-row ::deep a, .top-row ::deep .btn-link { + white-space: nowrap; + margin-left: 1.5rem; + text-decoration: none; + } + + .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { + text-decoration: underline; + } + + .top-row ::deep a:first-child { + overflow: hidden; + text-overflow: ellipsis; + } + +@media (max-width: 640.98px) { + .top-row:not(.auth) { + display: none; + } + + .top-row.auth { + justify-content: space-between; + } + + .top-row ::deep a, .top-row ::deep .btn-link { + margin-left: 0; + } +} + +@media (min-width: 641px) { + .page { + flex-direction: row; + } + + .sidebar { + width: 250px; + height: 100vh; + position: sticky; + top: 0; + } + + .top-row { + position: sticky; + top: 0; + z-index: 1; + } + + .top-row.auth ::deep a:first-child { + flex: 1; + width: 0; + } + + .top-row, article { + padding-left: 2rem !important; + padding-right: 1.5rem !important; + } +} diff --git a/samples/Blazor.ExampleConsumer/Shared/NavMenu.razor b/samples/Blazor.ExampleConsumer/Components/Layout/NavMenu.razor similarity index 100% rename from samples/Blazor.ExampleConsumer/Shared/NavMenu.razor rename to samples/Blazor.ExampleConsumer/Components/Layout/NavMenu.razor diff --git a/samples/Blazor.ExampleConsumer/Shared/NavMenu.razor.css b/samples/Blazor.ExampleConsumer/Components/Layout/NavMenu.razor.css similarity index 94% rename from samples/Blazor.ExampleConsumer/Shared/NavMenu.razor.css rename to samples/Blazor.ExampleConsumer/Components/Layout/NavMenu.razor.css index e681f238..acc5f9f8 100644 --- a/samples/Blazor.ExampleConsumer/Shared/NavMenu.razor.css +++ b/samples/Blazor.ExampleConsumer/Components/Layout/NavMenu.razor.css @@ -1,62 +1,62 @@ -.navbar-toggler { - background-color: rgba(255, 255, 255, 0.1); -} - -.top-row { - height: 3.5rem; - background-color: rgba(0,0,0,0.4); -} - -.navbar-brand { - font-size: 1.1rem; -} - -.oi { - width: 2rem; - font-size: 1.1rem; - vertical-align: text-top; - top: -2px; -} - -.nav-item { - font-size: 0.9rem; - padding-bottom: 0.5rem; -} - - .nav-item:first-of-type { - padding-top: 1rem; - } - - .nav-item:last-of-type { - padding-bottom: 1rem; - } - - .nav-item ::deep a { - color: #d7d7d7; - border-radius: 4px; - height: 3rem; - display: flex; - align-items: center; - line-height: 3rem; - } - -.nav-item ::deep a.active { - background-color: rgba(255,255,255,0.25); - color: white; -} - -.nav-item ::deep a:hover { - background-color: rgba(255,255,255,0.1); - color: white; -} - -@media (min-width: 641px) { - .navbar-toggler { - display: none; - } - - .collapse { - /* Never collapse the sidebar for wide screens */ - display: block; - } -} +.navbar-toggler { + background-color: rgba(255, 255, 255, 0.1); +} + +.top-row { + height: 3.5rem; + background-color: rgba(0,0,0,0.4); +} + +.navbar-brand { + font-size: 1.1rem; +} + +.oi { + width: 2rem; + font-size: 1.1rem; + vertical-align: text-top; + top: -2px; +} + +.nav-item { + font-size: 0.9rem; + padding-bottom: 0.5rem; +} + + .nav-item:first-of-type { + padding-top: 1rem; + } + + .nav-item:last-of-type { + padding-bottom: 1rem; + } + + .nav-item ::deep a { + color: #d7d7d7; + border-radius: 4px; + height: 3rem; + display: flex; + align-items: center; + line-height: 3rem; + } + +.nav-item ::deep a.active { + background-color: rgba(255,255,255,0.25); + color: white; +} + +.nav-item ::deep a:hover { + background-color: rgba(255,255,255,0.1); + color: white; +} + +@media (min-width: 641px) { + .navbar-toggler { + display: none; + } + + .collapse { + /* Never collapse the sidebar for wide screens */ + display: block; + } +} diff --git a/samples/Blazor.ExampleConsumer/Pages/ClientPosition.razor b/samples/Blazor.ExampleConsumer/Components/Pages/ClientPosition.razor similarity index 100% rename from samples/Blazor.ExampleConsumer/Pages/ClientPosition.razor rename to samples/Blazor.ExampleConsumer/Components/Pages/ClientPosition.razor diff --git a/samples/Blazor.ExampleConsumer/Pages/ClientPosition.razor.cs b/samples/Blazor.ExampleConsumer/Components/Pages/ClientPosition.razor.cs similarity index 96% rename from samples/Blazor.ExampleConsumer/Pages/ClientPosition.razor.cs rename to samples/Blazor.ExampleConsumer/Components/Pages/ClientPosition.razor.cs index 9b05e353..048d70a2 100644 --- a/samples/Blazor.ExampleConsumer/Pages/ClientPosition.razor.cs +++ b/samples/Blazor.ExampleConsumer/Components/Pages/ClientPosition.razor.cs @@ -1,7 +1,7 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. -namespace Blazor.ExampleConsumer.Pages; +namespace Blazor.ExampleConsumer.Components.Pages; public sealed partial class ClientPosition { diff --git a/samples/Blazor.ExampleConsumer/Pages/Index.razor b/samples/Blazor.ExampleConsumer/Components/Pages/Home.razor similarity index 87% rename from samples/Blazor.ExampleConsumer/Pages/Index.razor rename to samples/Blazor.ExampleConsumer/Components/Pages/Home.razor index 014471e9..41ba8cc1 100644 --- a/samples/Blazor.ExampleConsumer/Pages/Index.razor +++ b/samples/Blazor.ExampleConsumer/Components/Pages/Home.razor @@ -1,6 +1,6 @@ @page "/" -Index +Home

Hi friends! 🤓

diff --git a/samples/Blazor.ExampleConsumer/Pages/ListenToMe.razor b/samples/Blazor.ExampleConsumer/Components/Pages/ListenToMe.razor similarity index 100% rename from samples/Blazor.ExampleConsumer/Pages/ListenToMe.razor rename to samples/Blazor.ExampleConsumer/Components/Pages/ListenToMe.razor diff --git a/samples/Blazor.ExampleConsumer/Pages/ListenToMe.razor.cs b/samples/Blazor.ExampleConsumer/Components/Pages/ListenToMe.razor.cs similarity index 95% rename from samples/Blazor.ExampleConsumer/Pages/ListenToMe.razor.cs rename to samples/Blazor.ExampleConsumer/Components/Pages/ListenToMe.razor.cs index 13238ff0..45cb8452 100644 --- a/samples/Blazor.ExampleConsumer/Pages/ListenToMe.razor.cs +++ b/samples/Blazor.ExampleConsumer/Components/Pages/ListenToMe.razor.cs @@ -1,7 +1,7 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. -namespace Blazor.ExampleConsumer.Pages; +namespace Blazor.ExampleConsumer.Components.Pages; public sealed partial class ListenToMe : IDisposable { @@ -9,7 +9,7 @@ public sealed partial class ListenToMe : IDisposable IDisposable? _recognitionSubscription; bool _isRecognizingSpeech = false; - SpeechRecognitionErrorEvent? _errorEvent; + SpeechRecognitionErrorEvent? _errorEvent; string? _transcript; [Inject] diff --git a/samples/Blazor.ExampleConsumer/Pages/ReadToMe.razor b/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor similarity index 100% rename from samples/Blazor.ExampleConsumer/Pages/ReadToMe.razor rename to samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor diff --git a/samples/Blazor.ExampleConsumer/Pages/ReadToMe.razor.cs b/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor.cs similarity index 98% rename from samples/Blazor.ExampleConsumer/Pages/ReadToMe.razor.cs rename to samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor.cs index 09de22ba..ae8e1073 100644 --- a/samples/Blazor.ExampleConsumer/Pages/ReadToMe.razor.cs +++ b/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor.cs @@ -3,7 +3,7 @@ using Humanizer; -namespace Blazor.ExampleConsumer.Pages; +namespace Blazor.ExampleConsumer.Components.Pages; public sealed partial class ReadToMe : IDisposable { diff --git a/samples/Blazor.ExampleConsumer/Pages/Sandbox.razor b/samples/Blazor.ExampleConsumer/Components/Pages/Sandbox.razor similarity index 100% rename from samples/Blazor.ExampleConsumer/Pages/Sandbox.razor rename to samples/Blazor.ExampleConsumer/Components/Pages/Sandbox.razor diff --git a/samples/Blazor.ExampleConsumer/Pages/TodoList.razor b/samples/Blazor.ExampleConsumer/Components/Pages/TodoList.razor similarity index 100% rename from samples/Blazor.ExampleConsumer/Pages/TodoList.razor rename to samples/Blazor.ExampleConsumer/Components/Pages/TodoList.razor diff --git a/samples/Blazor.ExampleConsumer/Pages/TodoList.razor.cs b/samples/Blazor.ExampleConsumer/Components/Pages/TodoList.razor.cs similarity index 94% rename from samples/Blazor.ExampleConsumer/Pages/TodoList.razor.cs rename to samples/Blazor.ExampleConsumer/Components/Pages/TodoList.razor.cs index 17dcfd3a..b42034fa 100644 --- a/samples/Blazor.ExampleConsumer/Pages/TodoList.razor.cs +++ b/samples/Blazor.ExampleConsumer/Components/Pages/TodoList.razor.cs @@ -5,12 +5,12 @@ using Blazor.ExampleConsumer.Models; using Microsoft.AspNetCore.Components.Web; -namespace Blazor.ExampleConsumer.Pages; +namespace Blazor.ExampleConsumer.Components.Pages; public sealed partial class TodoList { - readonly Dictionary _localStorageItems = new(); - HashSet _todos = new(); + readonly Dictionary _localStorageItems = []; + HashSet _todos = []; string? _todoValue; [Inject] @@ -24,7 +24,7 @@ void UpdateTodoItems() .Where(key => key.StartsWith(TodoItem.IdPrefix)) .Select(key => LocalStorage.GetItem(key)) .Where(todo => todo is not null) - .ToHashSet() ?? new(); + .ToHashSet() ?? []; _todos = todos!; diff --git a/samples/Blazor.ExampleConsumer/Pages/Track.razor b/samples/Blazor.ExampleConsumer/Components/Pages/Track.razor similarity index 100% rename from samples/Blazor.ExampleConsumer/Pages/Track.razor rename to samples/Blazor.ExampleConsumer/Components/Pages/Track.razor diff --git a/samples/Blazor.ExampleConsumer/Pages/Track.razor.cs b/samples/Blazor.ExampleConsumer/Components/Pages/Track.razor.cs similarity index 96% rename from samples/Blazor.ExampleConsumer/Pages/Track.razor.cs rename to samples/Blazor.ExampleConsumer/Components/Pages/Track.razor.cs index 08c58a33..90f88e24 100644 --- a/samples/Blazor.ExampleConsumer/Pages/Track.razor.cs +++ b/samples/Blazor.ExampleConsumer/Components/Pages/Track.razor.cs @@ -1,7 +1,7 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. -namespace Blazor.ExampleConsumer.Pages; +namespace Blazor.ExampleConsumer.Components.Pages; public partial class Track : IDisposable { diff --git a/samples/Blazor.ExampleConsumer/Components/BingMap.razor b/samples/Blazor.ExampleConsumer/Components/Shared/BingMap.razor similarity index 100% rename from samples/Blazor.ExampleConsumer/Components/BingMap.razor rename to samples/Blazor.ExampleConsumer/Components/Shared/BingMap.razor diff --git a/samples/Blazor.ExampleConsumer/Components/Code.razor b/samples/Blazor.ExampleConsumer/Components/Shared/Code.razor similarity index 100% rename from samples/Blazor.ExampleConsumer/Components/Code.razor rename to samples/Blazor.ExampleConsumer/Components/Shared/Code.razor diff --git a/samples/Blazor.ExampleConsumer/Shared/RepositoryPrompt.razor b/samples/Blazor.ExampleConsumer/Components/Shared/RepositoryPrompt.razor similarity index 100% rename from samples/Blazor.ExampleConsumer/Shared/RepositoryPrompt.razor rename to samples/Blazor.ExampleConsumer/Components/Shared/RepositoryPrompt.razor diff --git a/samples/Blazor.ExampleConsumer/Components/StorageCheckbox.razor b/samples/Blazor.ExampleConsumer/Components/Shared/StorageCheckbox.razor similarity index 100% rename from samples/Blazor.ExampleConsumer/Components/StorageCheckbox.razor rename to samples/Blazor.ExampleConsumer/Components/Shared/StorageCheckbox.razor diff --git a/samples/Blazor.ExampleConsumer/_Imports.razor b/samples/Blazor.ExampleConsumer/Components/_Imports.razor similarity index 79% rename from samples/Blazor.ExampleConsumer/_Imports.razor rename to samples/Blazor.ExampleConsumer/Components/_Imports.razor index c2f28d78..d3a2689a 100644 --- a/samples/Blazor.ExampleConsumer/_Imports.razor +++ b/samples/Blazor.ExampleConsumer/Components/_Imports.razor @@ -10,6 +10,8 @@ @using System.Text.Json.Serialization @using Blazor.ExampleConsumer @using Blazor.ExampleConsumer.Models -@using Blazor.ExampleConsumer.Shared @using Blazor.ExampleConsumer.Components +@using Blazor.ExampleConsumer.Components.Layout +@using Blazor.ExampleConsumer.Components.Pages +@using Blazor.ExampleConsumer.Components.Shared @using Blazor.Serialization.Extensions \ No newline at end of file diff --git a/samples/Blazor.ExampleConsumer/GlobalUsings.cs b/samples/Blazor.ExampleConsumer/GlobalUsings.cs index 77998914..90801335 100644 --- a/samples/Blazor.ExampleConsumer/GlobalUsings.cs +++ b/samples/Blazor.ExampleConsumer/GlobalUsings.cs @@ -3,6 +3,6 @@ global using System.Text.Json; global using System.Text.Json.Serialization; -global using Microsoft.JSInterop; global using Microsoft.AspNetCore.Components; +global using Microsoft.JSInterop; global using static System.Globalization.CultureInfo; diff --git a/samples/Blazor.ExampleConsumer/Models/TodoItem.cs b/samples/Blazor.ExampleConsumer/Models/TodoItem.cs index f64171a5..c5525678 100644 --- a/samples/Blazor.ExampleConsumer/Models/TodoItem.cs +++ b/samples/Blazor.ExampleConsumer/Models/TodoItem.cs @@ -8,7 +8,7 @@ namespace Blazor.ExampleConsumer.Models; public partial record class TodoItem( string Task, bool IsCompleted) -{ +{ internal const string IdPrefix = "todo"; [JsonIgnore] diff --git a/samples/Blazor.ExampleConsumer/Program.cs b/samples/Blazor.ExampleConsumer/Program.cs index 18822f0c..e1e070fa 100644 --- a/samples/Blazor.ExampleConsumer/Program.cs +++ b/samples/Blazor.ExampleConsumer/Program.cs @@ -1,4 +1,7 @@ -using Blazor.ExampleConsumer; +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. + +using Blazor.ExampleConsumer.Components; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; diff --git a/samples/Blazor.ExampleConsumer/Shared/MainLayout.razor b/samples/Blazor.ExampleConsumer/Shared/MainLayout.razor deleted file mode 100644 index 042aba3c..00000000 --- a/samples/Blazor.ExampleConsumer/Shared/MainLayout.razor +++ /dev/null @@ -1,14 +0,0 @@ -@inherits LayoutComponentBase - -
- - -
-
-
- @Body -
-
-
diff --git a/samples/Blazor.ExampleConsumer/wwwroot/index.html b/samples/Blazor.ExampleConsumer/wwwroot/index.html index 151f7d38..d74cc3b9 100644 --- a/samples/Blazor.ExampleConsumer/wwwroot/index.html +++ b/samples/Blazor.ExampleConsumer/wwwroot/index.html @@ -31,6 +31,7 @@
🤖 Loading... +
diff --git a/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj b/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj index 60ed3755..138ed72f 100644 --- a/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj +++ b/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj @@ -1,16 +1,16 @@ - + - net7.0 + net8.0 enable enable - - - + + + - + diff --git a/samples/BlazorServer.ExampleConsumer/GlobalUsings.cs b/samples/BlazorServer.ExampleConsumer/GlobalUsings.cs index 05bd19d0..580cd479 100644 --- a/samples/BlazorServer.ExampleConsumer/GlobalUsings.cs +++ b/samples/BlazorServer.ExampleConsumer/GlobalUsings.cs @@ -1,13 +1,15 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +global using System.Diagnostics; global using System.Text.Json; global using System.Text.Json.Serialization; -global using Microsoft.JSInterop; -global using Microsoft.AspNetCore.Components; -global using BlazorServer.ExampleConsumer; -global using BlazorServer.ExampleConsumer.Components; + global using BlazorServer.ExampleConsumer.Models; -global using BlazorServer.ExampleConsumer.Pages; -global using BlazorServer.ExampleConsumer.Shared; + +global using Microsoft.AspNetCore.Components; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.AspNetCore.Mvc.RazorPages; +global using Microsoft.JSInterop; + global using static System.Globalization.CultureInfo; diff --git a/samples/BlazorServer.ExampleConsumer/Pages/Error.cshtml.cs b/samples/BlazorServer.ExampleConsumer/Pages/Error.cshtml.cs index 45afe859..8a74da4c 100644 --- a/samples/BlazorServer.ExampleConsumer/Pages/Error.cshtml.cs +++ b/samples/BlazorServer.ExampleConsumer/Pages/Error.cshtml.cs @@ -1,11 +1,8 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. -using System.Diagnostics; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - namespace BlazorServer.ExampleConsumer.Pages; + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] [IgnoreAntiforgeryToken] public class ErrorModel : PageModel @@ -14,13 +11,6 @@ public class ErrorModel : PageModel public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - private readonly ILogger _logger; - - public ErrorModel(ILogger logger) - { - _logger = logger; - } - public void OnGet() { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; diff --git a/samples/BlazorServer.ExampleConsumer/Pages/ListenToMe.razor.cs b/samples/BlazorServer.ExampleConsumer/Pages/ListenToMe.razor.cs index d331eb70..bc88eec7 100644 --- a/samples/BlazorServer.ExampleConsumer/Pages/ListenToMe.razor.cs +++ b/samples/BlazorServer.ExampleConsumer/Pages/ListenToMe.razor.cs @@ -9,7 +9,7 @@ public sealed partial class ListenToMe : IDisposable IDisposable? _recognitionSubscription; bool _isRecognizingSpeech = false; - SpeechRecognitionErrorEvent? _errorEvent; + SpeechRecognitionErrorEvent? _errorEvent; string? _transcript; [Inject] diff --git a/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor.cs b/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor.cs index 579aa62b..4e5ff215 100644 --- a/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor.cs +++ b/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor.cs @@ -1,8 +1,6 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. -using Humanizer; - namespace BlazorServer.ExampleConsumer.Pages; public sealed partial class ReadToMe : IAsyncDisposable diff --git a/samples/BlazorServer.ExampleConsumer/Pages/TodoList.razor.cs b/samples/BlazorServer.ExampleConsumer/Pages/TodoList.razor.cs index bb320453..2be3f4a1 100644 --- a/samples/BlazorServer.ExampleConsumer/Pages/TodoList.razor.cs +++ b/samples/BlazorServer.ExampleConsumer/Pages/TodoList.razor.cs @@ -1,18 +1,15 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. -using System; -using System.Diagnostics.CodeAnalysis; using Blazor.Serialization.Extensions; -using BlazorServer.ExampleConsumer.Models; using Microsoft.AspNetCore.Components.Web; namespace BlazorServer.ExampleConsumer.Pages; public sealed partial class TodoList { - readonly Dictionary _localStorageItems = new(); - HashSet _todos = new(); + readonly Dictionary _localStorageItems = []; + HashSet _todos = []; string? _todoValue; [Inject] @@ -30,7 +27,7 @@ protected override Task OnAfterRenderAsync(bool firstRender) async Task UpdateTodoItemsAsync() { - HashSet todos = new(); + HashSet todos = []; await foreach (var key in GetLocalStorageKeysAsync()) { if (key.StartsWith(TodoItem.IdPrefix)) diff --git a/src/Blazor.Geolocation.WebAssembly/Blazor.Geolocation.WebAssembly.csproj b/src/Blazor.Geolocation.WebAssembly/Blazor.Geolocation.WebAssembly.csproj index 91f2b0c9..766ead73 100644 --- a/src/Blazor.Geolocation.WebAssembly/Blazor.Geolocation.WebAssembly.csproj +++ b/src/Blazor.Geolocation.WebAssembly/Blazor.Geolocation.WebAssembly.csproj @@ -1,77 +1,77 @@ - - $(DefaultTargetFrameworks) - enable - enable - Source generated JavaScript interop for the browser's geolocation API compatible with Blazor WebAssembly. - Copyright © David Pine. All rights reserved. Licensed under the MIT License. - en-US - $([System.DateTime]::Now.ToString(yyyyMMdd)) - $(ClientOfficialVersion) - $(ClientPreviewVersion) - nightly-$(CurrentDate) - preview - $(ClientVersion) - $(ClientVersion)-$(VersionSuffix) - $(ClientVersion) - David Pine - - Blazor.Geolocation.WebAssembly - A C# source-generated Razor class library implementation of the native browser's geolocation API available as a DI-ready service. - Blazor.Geolocation.WebAssembly - dotnet;dotnetcore;csharp;blazor;webassembly;wasm;generators;sourcegen;roslyn; - - https://github.com/IEvangelist/blazorators - true - true - false - true - AnyCPU - External - Product - embedded - false - false - Blazor.Geolocation.WebAssembly - NU5125;NU5039; - true - https://github.com/IEvangelist/blazorators - LICENSE - git - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true - README.md - true - logo.png - + + $(DefaultTargetFrameworks) + enable + enable + Source generated JavaScript interop for the browser's geolocation API compatible with Blazor WebAssembly. + Copyright © David Pine. All rights reserved. Licensed under the MIT License. + en-US + $([System.DateTime]::Now.ToString(yyyyMMdd)) + $(ClientOfficialVersion) + $(ClientPreviewVersion) + nightly-$(CurrentDate) + preview + $(ClientVersion) + $(ClientVersion)-$(VersionSuffix) + $(ClientVersion) + David Pine + + Blazor.Geolocation.WebAssembly + A C# source-generated Razor class library implementation of the native browser's geolocation API available as a DI-ready service. + Blazor.Geolocation.WebAssembly + dotnet;dotnetcore;csharp;blazor;webassembly;wasm;generators;sourcegen;roslyn; + + https://github.com/IEvangelist/blazorators + true + true + false + true + AnyCPU + External + Product + embedded + false + false + Blazor.Geolocation.WebAssembly + NU5125;NU5039; + true + https://github.com/IEvangelist/blazorators + LICENSE + git + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + true + README.md + true + logo.png + - - - + + + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + - - - - + + + + - - - - - + + + + + diff --git a/src/Blazor.Geolocation/Blazor.Geolocation.csproj b/src/Blazor.Geolocation/Blazor.Geolocation.csproj index 91873d5c..813f855d 100644 --- a/src/Blazor.Geolocation/Blazor.Geolocation.csproj +++ b/src/Blazor.Geolocation/Blazor.Geolocation.csproj @@ -1,81 +1,81 @@ - - $(DefaultTargetFrameworks) - enable - enable - Source generated JavaScript interop for the browser's geolocation API compatible with Blazor. - Copyright © David Pine. All rights reserved. Licensed under the MIT License. - en-US - $([System.DateTime]::Now.ToString(yyyyMMdd)) - $(ClientOfficialVersion) - $(ClientPreviewVersion) - nightly-$(CurrentDate) - preview - $(ClientVersion) - $(ClientVersion)-$(VersionSuffix) - $(ClientVersion) - David Pine - - Blazor.Geolocation - A C# source-generated Razor class library implementation of the native browser's geolocation API available as a DI-ready service. - Blazor.Geolocation - dotnet;dotnetcore;csharp;blazor;server;wasm;generators;sourcegen;roslyn; - - https://github.com/IEvangelist/blazorators - true - true - false - true - AnyCPU - External - Product - embedded - false - false - Blazor.Geolocation - NU5125;NU5039; - true - https://github.com/IEvangelist/blazorators - LICENSE - git - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true - README.md - true - logo.png - + + $(DefaultTargetFrameworks) + enable + enable + Source generated JavaScript interop for the browser's geolocation API compatible with Blazor. + Copyright © David Pine. All rights reserved. Licensed under the MIT License. + en-US + $([System.DateTime]::Now.ToString(yyyyMMdd)) + $(ClientOfficialVersion) + $(ClientPreviewVersion) + nightly-$(CurrentDate) + preview + $(ClientVersion) + $(ClientVersion)-$(VersionSuffix) + $(ClientVersion) + David Pine + + Blazor.Geolocation + A C# source-generated Razor class library implementation of the native browser's geolocation API available as a DI-ready service. + Blazor.Geolocation + dotnet;dotnetcore;csharp;blazor;server;wasm;generators;sourcegen;roslyn; + + https://github.com/IEvangelist/blazorators + true + true + false + true + AnyCPU + External + Product + embedded + false + false + Blazor.Geolocation + NU5125;NU5039; + true + https://github.com/IEvangelist/blazorators + LICENSE + git + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + true + README.md + true + logo.png + - - - + + + - - - + + + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + - - - - + + + + - - - - - + + + + + diff --git a/src/Blazor.LocalStorage.WebAssembly/Blazor.LocalStorage.WebAssembly.csproj b/src/Blazor.LocalStorage.WebAssembly/Blazor.LocalStorage.WebAssembly.csproj index a7889450..71b23597 100644 --- a/src/Blazor.LocalStorage.WebAssembly/Blazor.LocalStorage.WebAssembly.csproj +++ b/src/Blazor.LocalStorage.WebAssembly/Blazor.LocalStorage.WebAssembly.csproj @@ -1,75 +1,75 @@  - - $(DefaultTargetFrameworks) - enable - enable - Source generated JavaScript interop for the browser's localStorage API compatible with Blazor WebAssembly. - Copyright © David Pine. All rights reserved. Licensed under the MIT License. - en-US - $([System.DateTime]::Now.ToString(yyyyMMdd)) - $(ClientOfficialVersion) - $(ClientPreviewVersion) - nightly-$(CurrentDate) - preview - $(ClientVersion) - $(ClientVersion)-$(VersionSuffix) - $(ClientVersion) - David Pine - true - Blazor.LocalStorage.WebAssembly - A C# source-generated class library implementation of the native browser's localStorage API available as IJSInProcessRuntime extension methods. - Blazor.LocalStorage.WebAssembly - dotnet;dotnetcore;csharp;blazor;webassembly;wasm;generators;sourcegen;roslyn; - - https://github.com/IEvangelist/blazorators - true - true - false - true - AnyCPU - External - Product - embedded - false - false - Blazor.LocalStorage.WebAssembly - NU5125;NU5039; - true - https://github.com/IEvangelist/blazorators - LICENSE - git - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true - README.md - true - logo.png - + + $(DefaultTargetFrameworks) + enable + enable + Source generated JavaScript interop for the browser's localStorage API compatible with Blazor WebAssembly. + Copyright © David Pine. All rights reserved. Licensed under the MIT License. + en-US + $([System.DateTime]::Now.ToString(yyyyMMdd)) + $(ClientOfficialVersion) + $(ClientPreviewVersion) + nightly-$(CurrentDate) + preview + $(ClientVersion) + $(ClientVersion)-$(VersionSuffix) + $(ClientVersion) + David Pine + true + Blazor.LocalStorage.WebAssembly + A C# source-generated class library implementation of the native browser's localStorage API available as IJSInProcessRuntime extension methods. + Blazor.LocalStorage.WebAssembly + dotnet;dotnetcore;csharp;blazor;webassembly;wasm;generators;sourcegen;roslyn; + + https://github.com/IEvangelist/blazorators + true + true + false + true + AnyCPU + External + Product + embedded + false + false + Blazor.LocalStorage.WebAssembly + NU5125;NU5039; + true + https://github.com/IEvangelist/blazorators + LICENSE + git + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + true + README.md + true + logo.png + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + - - - - + + + + - - - - - + + + + + diff --git a/src/Blazor.LocalStorage/Blazor.LocalStorage.csproj b/src/Blazor.LocalStorage/Blazor.LocalStorage.csproj index 562aeeda..0a78434e 100644 --- a/src/Blazor.LocalStorage/Blazor.LocalStorage.csproj +++ b/src/Blazor.LocalStorage/Blazor.LocalStorage.csproj @@ -1,74 +1,74 @@ - - $(DefaultTargetFrameworks) - enable - enable - Source generated JavaScript interop for the browser's localStorage API compatible with Blazor. - Copyright © David Pine. All rights reserved. Licensed under the MIT License. - en-US - $([System.DateTime]::Now.ToString(yyyyMMdd)) - $(ClientOfficialVersion) - $(ClientPreviewVersion) - nightly-$(CurrentDate) - preview - $(ClientVersion) - $(ClientVersion)-$(VersionSuffix) - $(ClientVersion) - David Pine - true - Blazor.LocalStorage - A C# source-generated class library implementation of the native browser's localStorage API available as IJSRuntime extension methods. - Blazor.LocalStorage - dotnet;dotnetcore;csharp;blazor;generators;sourcegen;roslyn; - - https://github.com/IEvangelist/blazorators - true - true - false - true - AnyCPU - External - Product - embedded - false - false - Blazor.LocalStorage - NU5125;NU5039 - true - https://github.com/IEvangelist/blazorators - LICENSE - git - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true - README.md - true - logo.png - + + $(DefaultTargetFrameworks) + enable + enable + Source generated JavaScript interop for the browser's localStorage API compatible with Blazor. + Copyright © David Pine. All rights reserved. Licensed under the MIT License. + en-US + $([System.DateTime]::Now.ToString(yyyyMMdd)) + $(ClientOfficialVersion) + $(ClientPreviewVersion) + nightly-$(CurrentDate) + preview + $(ClientVersion) + $(ClientVersion)-$(VersionSuffix) + $(ClientVersion) + David Pine + true + Blazor.LocalStorage + A C# source-generated class library implementation of the native browser's localStorage API available as IJSRuntime extension methods. + Blazor.LocalStorage + dotnet;dotnetcore;csharp;blazor;generators;sourcegen;roslyn; + + https://github.com/IEvangelist/blazorators + true + true + false + true + AnyCPU + External + Product + embedded + false + false + Blazor.LocalStorage + NU5125;NU5039 + true + https://github.com/IEvangelist/blazorators + LICENSE + git + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + true + README.md + true + logo.png + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + - - - + + + - - - - - + + + + + diff --git a/src/Blazor.Permissions.WebAssembly/Blazor.Permissions.WebAssembly.csproj b/src/Blazor.Permissions.WebAssembly/Blazor.Permissions.WebAssembly.csproj index 1d8419fc..28f8c5d1 100644 --- a/src/Blazor.Permissions.WebAssembly/Blazor.Permissions.WebAssembly.csproj +++ b/src/Blazor.Permissions.WebAssembly/Blazor.Permissions.WebAssembly.csproj @@ -1,74 +1,74 @@ - - $(DefaultTargetFrameworks) - enable - enable - Source generated JavaScript interop for the browser's permissions API compatible with Blazor WebAssembly. - Copyright © David Pine. All rights reserved. Licensed under the MIT License. - en-US - $([System.DateTime]::Now.ToString(yyyyMMdd)) - $(ClientOfficialVersion) - $(ClientPreviewVersion) - nightly-$(CurrentDate) - preview - $(ClientVersion) - $(ClientVersion)-$(VersionSuffix) - $(ClientVersion) - David Pine - true - Blazor.Permissions.WebAssembly - A C# source-generated class library implementation of the native browser's permissions API available as IJSInProcessRuntime extension methods. - Blazor.Permissions.WebAssembly - dotnet;dotnetcore;csharp;blazor;webassembly;wasm;generators;sourcegen;roslyn; - - https://github.com/IEvangelist/blazorators - true - true - false - true - AnyCPU - External - Product - embedded - false - false - Blazor.Permissions.WebAssembly - NU5125;NU5039; - true - https://github.com/IEvangelist/blazorators - LICENSE - git - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true - README.md - true - logo.png - + + $(DefaultTargetFrameworks) + enable + enable + Source generated JavaScript interop for the browser's permissions API compatible with Blazor WebAssembly. + Copyright © David Pine. All rights reserved. Licensed under the MIT License. + en-US + $([System.DateTime]::Now.ToString(yyyyMMdd)) + $(ClientOfficialVersion) + $(ClientPreviewVersion) + nightly-$(CurrentDate) + preview + $(ClientVersion) + $(ClientVersion)-$(VersionSuffix) + $(ClientVersion) + David Pine + true + Blazor.Permissions.WebAssembly + A C# source-generated class library implementation of the native browser's permissions API available as IJSInProcessRuntime extension methods. + Blazor.Permissions.WebAssembly + dotnet;dotnetcore;csharp;blazor;webassembly;wasm;generators;sourcegen;roslyn; + + https://github.com/IEvangelist/blazorators + true + true + false + true + AnyCPU + External + Product + embedded + false + false + Blazor.Permissions.WebAssembly + NU5125;NU5039; + true + https://github.com/IEvangelist/blazorators + LICENSE + git + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + true + README.md + true + logo.png + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + - - - + + + - - - - - + + + + + diff --git a/src/Blazor.Permissions.WebAssembly/IPermissionsService.cs b/src/Blazor.Permissions.WebAssembly/IPermissionsService.cs index 7658215a..586af654 100644 --- a/src/Blazor.Permissions.WebAssembly/IPermissionsService.cs +++ b/src/Blazor.Permissions.WebAssembly/IPermissionsService.cs @@ -1,4 +1,7 @@ -namespace Microsoft.JSInterop; +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.JSInterop; /// //[JSAutoInterop( diff --git a/src/Blazor.Serialization/Blazor.Serialization.csproj b/src/Blazor.Serialization/Blazor.Serialization.csproj index 155169b2..37171417 100644 --- a/src/Blazor.Serialization/Blazor.Serialization.csproj +++ b/src/Blazor.Serialization/Blazor.Serialization.csproj @@ -1,67 +1,67 @@ - - $(DefaultTargetFrameworks) - enable - enable - A C# class library providing light-weight serialization functionality. - Copyright © David Pine. All rights reserved. Licensed under the MIT License. - en-US - $([System.DateTime]::Now.ToString(yyyyMMdd)) - $(ClientOfficialVersion) - $(ClientPreviewVersion) - nightly-$(CurrentDate) - preview - $(ClientVersion) - $(ClientVersion)-$(VersionSuffix) - $(ClientVersion) - David Pine - true - Blazor.Serialization - Blazor serialization class library - Blazor.Serialization - dotnet;dotnetcore;csharp;blazor;webassembly;wasm;generators;sourcegen;roslyn; - - https://github.com/IEvangelist/blazorators - true - true - false - true - AnyCPU - External - Product - embedded - false - false - Blazor.Serialization - NU5125;NU5039; - true - https://github.com/IEvangelist/blazorators - LICENSE - git - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true - README.md - true - logo.png - + + $(DefaultTargetFrameworks) + enable + enable + A C# class library providing light-weight serialization functionality. + Copyright © David Pine. All rights reserved. Licensed under the MIT License. + en-US + $([System.DateTime]::Now.ToString(yyyyMMdd)) + $(ClientOfficialVersion) + $(ClientPreviewVersion) + nightly-$(CurrentDate) + preview + $(ClientVersion) + $(ClientVersion)-$(VersionSuffix) + $(ClientVersion) + David Pine + true + Blazor.Serialization + Blazor serialization class library + Blazor.Serialization + dotnet;dotnetcore;csharp;blazor;webassembly;wasm;generators;sourcegen;roslyn; + + https://github.com/IEvangelist/blazorators + true + true + false + true + AnyCPU + External + Product + embedded + false + false + Blazor.Serialization + NU5125;NU5039; + true + https://github.com/IEvangelist/blazorators + LICENSE + git + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + true + README.md + true + logo.png + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - - - - - + + + + + diff --git a/src/Blazor.SessionStorage.WebAssembly/Blazor.SessionStorage.WebAssembly.csproj b/src/Blazor.SessionStorage.WebAssembly/Blazor.SessionStorage.WebAssembly.csproj index ddce3326..ce4114ec 100644 --- a/src/Blazor.SessionStorage.WebAssembly/Blazor.SessionStorage.WebAssembly.csproj +++ b/src/Blazor.SessionStorage.WebAssembly/Blazor.SessionStorage.WebAssembly.csproj @@ -1,75 +1,75 @@ - - $(DefaultTargetFrameworks) - enable - enable - Source generated JavaScript interop for the browser's sessionStorage API compatible with Blazor WebAssembly. - Copyright © David Pine. All rights reserved. Licensed under the MIT License. - en-US - $([System.DateTime]::Now.ToString(yyyyMMdd)) - $(ClientOfficialVersion) - $(ClientPreviewVersion) - nightly-$(CurrentDate) - preview - $(ClientVersion) - $(ClientVersion)-$(VersionSuffix) - $(ClientVersion) - David Pine - true - Blazor.SessionStorage.WebAssembly - A C# source-generated class library implementation of the native browser's sessionStorage API available as IJSInProcessRuntime extension methods. - Blazor.SessionStorage.WebAssembly - dotnet;dotnetcore;csharp;blazor;webassembly;wasm;generators;sourcegen;roslyn; - - https://github.com/IEvangelist/blazorators - true - true - false - true - AnyCPU - External - Product - embedded - false - false - Blazor.SessionStorage.WebAssembly - NU5125;NU5039; - true - https://github.com/IEvangelist/blazorators - LICENSE - git - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true - README.md - true - logo.png - + + $(DefaultTargetFrameworks) + enable + enable + Source generated JavaScript interop for the browser's sessionStorage API compatible with Blazor WebAssembly. + Copyright © David Pine. All rights reserved. Licensed under the MIT License. + en-US + $([System.DateTime]::Now.ToString(yyyyMMdd)) + $(ClientOfficialVersion) + $(ClientPreviewVersion) + nightly-$(CurrentDate) + preview + $(ClientVersion) + $(ClientVersion)-$(VersionSuffix) + $(ClientVersion) + David Pine + true + Blazor.SessionStorage.WebAssembly + A C# source-generated class library implementation of the native browser's sessionStorage API available as IJSInProcessRuntime extension methods. + Blazor.SessionStorage.WebAssembly + dotnet;dotnetcore;csharp;blazor;webassembly;wasm;generators;sourcegen;roslyn; + + https://github.com/IEvangelist/blazorators + true + true + false + true + AnyCPU + External + Product + embedded + false + false + Blazor.SessionStorage.WebAssembly + NU5125;NU5039; + true + https://github.com/IEvangelist/blazorators + LICENSE + git + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + true + README.md + true + logo.png + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + - - - - + + + + - - - - - + + + + + diff --git a/src/Blazor.SessionStorage/Blazor.SessionStorage.csproj b/src/Blazor.SessionStorage/Blazor.SessionStorage.csproj index 8d097229..c88ab40d 100644 --- a/src/Blazor.SessionStorage/Blazor.SessionStorage.csproj +++ b/src/Blazor.SessionStorage/Blazor.SessionStorage.csproj @@ -1,74 +1,74 @@ - - $(DefaultTargetFrameworks) - enable - enable - Source generated JavaScript interop for the browser's sessionStorage API compatible with Blazor. - Copyright © David Pine. All rights reserved. Licensed under the MIT License. - en-US - $([System.DateTime]::Now.ToString(yyyyMMdd)) - $(ClientOfficialVersion) - $(ClientPreviewVersion) - nightly-$(CurrentDate) - preview - $(ClientVersion) - $(ClientVersion)-$(VersionSuffix) - $(ClientVersion) - David Pine - true - Blazor.SessionStorage - A C# source-generated class library implementation of the native browser's sessionStorage API available as IJSRuntime extension methods. - Blazor.SessionStorage - dotnet;dotnetcore;csharp;blazor;generators;sourcegen;roslyn; - - https://github.com/IEvangelist/blazorators - true - true - false - true - AnyCPU - External - Product - embedded - false - false - Blazor.SessionStorage - NU5125;NU5039 - true - https://github.com/IEvangelist/blazorators - LICENSE - git - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true - README.md - true - logo.png - + + $(DefaultTargetFrameworks) + enable + enable + Source generated JavaScript interop for the browser's sessionStorage API compatible with Blazor. + Copyright © David Pine. All rights reserved. Licensed under the MIT License. + en-US + $([System.DateTime]::Now.ToString(yyyyMMdd)) + $(ClientOfficialVersion) + $(ClientPreviewVersion) + nightly-$(CurrentDate) + preview + $(ClientVersion) + $(ClientVersion)-$(VersionSuffix) + $(ClientVersion) + David Pine + true + Blazor.SessionStorage + A C# source-generated class library implementation of the native browser's sessionStorage API available as IJSRuntime extension methods. + Blazor.SessionStorage + dotnet;dotnetcore;csharp;blazor;generators;sourcegen;roslyn; + + https://github.com/IEvangelist/blazorators + true + true + false + true + AnyCPU + External + Product + embedded + false + false + Blazor.SessionStorage + NU5125;NU5039 + true + https://github.com/IEvangelist/blazorators + LICENSE + git + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + true + README.md + true + logo.png + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + - - - + + + - - - - - + + + + + diff --git a/src/Blazor.SourceGenerators/AnalyzerReleases.Shipped.md b/src/Blazor.SourceGenerators/AnalyzerReleases.Shipped.md new file mode 100644 index 00000000..60b59dd9 --- /dev/null +++ b/src/Blazor.SourceGenerators/AnalyzerReleases.Shipped.md @@ -0,0 +1,3 @@ +; Shipped analyzer releases +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + diff --git a/src/Blazor.SourceGenerators/AnalyzerReleases.Unshipped.md b/src/Blazor.SourceGenerators/AnalyzerReleases.Unshipped.md new file mode 100644 index 00000000..f2ab206c --- /dev/null +++ b/src/Blazor.SourceGenerators/AnalyzerReleases.Unshipped.md @@ -0,0 +1,12 @@ +; Unshipped analyzer release +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +BR0001 | Blazorators.JSAutoInteropAttribute | Error | Descriptors +BR0002 | Blazorators.JSAutoInteropAttribute | Error | Descriptors +BR0003 | Blazorators.JSAutoGenericInteropAttribute | Error | Descriptors +BR0004 | Blazorators.JSAutoGenericInteropAttribute | Error | Descriptors +BR0005 | Blazor.SourceGenerators.JavaScriptInteropGenerator.TryExecute | Warning | Descriptors \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Blazor.SourceGenerators.csproj b/src/Blazor.SourceGenerators/Blazor.SourceGenerators.csproj index 595e89ea..eff104ec 100644 --- a/src/Blazor.SourceGenerators/Blazor.SourceGenerators.csproj +++ b/src/Blazor.SourceGenerators/Blazor.SourceGenerators.csproj @@ -1,103 +1,96 @@  - - netstandard2.0 - latest - enable - enable - true - Copyright © David Pine. All rights reserved. Licensed under the MIT License. + + netstandard2.0 + preview + enable + enable + true + Copyright © David Pine. All rights reserved. Licensed under the MIT License. + true - - true - - false + + true + + false - en-US - $([System.DateTime]::Now.ToString(yyyyMMdd)) - $(ClientOfficialVersion) - $(ClientPreviewVersion) - nightly-$(CurrentDate) - preview - $(ClientVersion) - $(ClientVersion)-$(VersionSuffix) - $(ClientVersion) - David Pine - true - Blazor.SourceGenerators - Blazor Source Generators - The source generator used to generate extension methods on the IJSInProcessRuntime type for WebAssembly JavaScript interop. - Blazor.SourceGenerators - Blazor.SourceGenerators - dotnet;dotnetcore;csharp;analyzers;sourcegen;generators;roslyn; - - https://github.com/IEvangelist/blazorators - true - true - false - true - AnyCPU - External - Product - embedded - false - false - NU5125;NU5039 - true - https://github.com/IEvangelist/blazorators - LICENSE - git - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true - README.md - true - logo.png - true - + en-US + $([System.DateTime]::Now.ToString(yyyyMMdd)) + $(ClientOfficialVersion) + $(ClientPreviewVersion) + nightly-$(CurrentDate) + preview + $(ClientVersion) + $(ClientVersion)-$(VersionSuffix) + $(ClientVersion) + David Pine + true + Blazor.SourceGenerators + Blazor Source Generators + The source generator used to generate extension methods on the IJSInProcessRuntime type for WebAssembly JavaScript interop. + Blazor.SourceGenerators + Blazor.SourceGenerators + dotnet;dotnetcore;csharp;analyzers;sourcegen;generators;roslyn; + + https://github.com/IEvangelist/blazorators + true + true + false + true + AnyCPU + External + Product + embedded + false + false + NU5125;NU5039 + true + https://github.com/IEvangelist/blazorators + LICENSE + git + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + true + README.md + true + logo.png + true + - - 1701;1702;NU5128 - + + 1701;1702;NU5128 + - - 1701;1702;NU5128 - + + + - - - - + + + - - - - + + + + - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - + + + + + diff --git a/src/Blazor.SourceGenerators/Builders/Indentation.cs b/src/Blazor.SourceGenerators/Builders/Indentation.cs index bd58c316..25765bb0 100644 --- a/src/Blazor.SourceGenerators/Builders/Indentation.cs +++ b/src/Blazor.SourceGenerators/Builders/Indentation.cs @@ -1,15 +1,46 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. -internal readonly record struct Indentation(int Level) +/// +/// Represents the indentation level and spaces for generating code. +/// +/// The current indentation level. +/// The number of spaces to use for indentation. +internal readonly record struct Indentation(int Level, int Spaces = 4) { - private readonly int _spaces = 4; - + /// + /// Resets the indentation level to 0. + /// + /// A new instance with a level of 0. internal Indentation Reset() => ResetTo(0); + + /// + /// Resets the indentation level to the specified value. + /// + /// The new indentation level. + /// A new instance with the updated level. internal Indentation ResetTo(int level) => this with { Level = level }; + + /// + /// Increases the indentation level by one, with an optional extra increment. + /// + /// An optional extra increment to add to the indentation level. + /// A new instance with the incremented indentation level. internal Indentation Increase(int extra = 0) => this with { Level = Level + 1 + extra }; + + /// + /// Decreases the indentation level by the specified amount. + /// + /// The additional amount to decrease the indentation level by. + /// A new instance with the decremented indentation level. internal Indentation Decrease(int extra = 0) => this with { Level = Level - 1 - extra }; - public override string ToString() => - new(' ', _spaces * Level); + /// + /// Returns a representation of the current indentation level. + /// + /// + /// This is used to generate the indentation for the code. For example, if the indentation + /// level is 2 and the number of spaces is 4, then the result would be " ". + /// + public override string ToString() => new(' ', Spaces * Level); } diff --git a/src/Blazor.SourceGenerators/Builders/IndentationAdjustment.cs b/src/Blazor.SourceGenerators/Builders/IndentationAdjustment.cs index fd407cb5..9da58575 100644 --- a/src/Blazor.SourceGenerators/Builders/IndentationAdjustment.cs +++ b/src/Blazor.SourceGenerators/Builders/IndentationAdjustment.cs @@ -3,9 +3,23 @@ namespace Blazor.SourceGenerators.Builders; +/// +/// Specifies the type of adjustment to be made to the indentation level. +/// internal enum IndentationAdjustment { - Noop, + /// + /// No adjustment is made to the indentation level. + /// + NoOp, + + /// + /// The indentation level is increased. + /// Increase, + + /// + /// The indentation level is decreased. + /// Decrease }; \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs b/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs index 156fcc98..af237096 100644 --- a/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs +++ b/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs @@ -1,8 +1,25 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +using Blazor.SourceGenerators.Options; + namespace Blazor.SourceGenerators.Builders; +/// +/// Represents the details of a method builder, including information about the method's return type, name, and parameters. +/// +/// The to generate code for. +/// A value indicating whether the method returns . +/// A value indicating whether the method returns a primitive type. +/// A value indicating whether the method returns a generic type. +/// A value indicating whether the method contains generic parameters. +/// The name of the method. +/// The fully qualified JavaScript identifier. +/// The return type of the method. +/// The bare type of the method. +/// The suffix to append to the method name. +/// The type to extend. +/// The generic type arguments. internal readonly record struct MethodBuilderDetails( CSharpMethod Method, bool IsVoid, @@ -27,47 +44,37 @@ internal readonly record struct MethodBuilderDetails( /// internal const string GenericComponentType = "TComponent"; - internal static readonly Func ToGenericTypeArgument = - string (string value) => $"<{value}>"; + /// + /// Returns a string representing a generic type argument with the specified value. + /// + internal static readonly Func ToGenericTypeArgument = static string (string value) => $"<{value}>"; + /// + /// Gets a value indicating whether the method's return type is serializable. + /// internal bool IsSerializable => IsGenericReturnType || ContainsGenericParameters; + /// + /// Creates a new instance of based on the provided and . + /// + /// The to create the from. + /// The to use when creating the . + /// A new instance of based on the provided and . internal static MethodBuilderDetails Create(CSharpMethod method, GeneratorOptions options) { var isGenericReturnType = method.IsGenericReturnType(options); - var isPrimitiveType = TypeMap.PrimitiveTypes.IsPrimitiveType(method.RawReturnTypeName); - var containsGenericParameters = - method.ParameterDefinitions.Any(p => p.IsGenericParameter(method.RawName, options)); - var genericTypeArgs = isGenericReturnType - ? ToGenericTypeArgument(GenericTypeValue) - : containsGenericParameters ? ToGenericTypeArgument(GenericTypeValue) : null; - var fullyQualifiedJavaScriptIdentifier = method.JavaScriptMethodDependency?.InvokableMethodName; - fullyQualifiedJavaScriptIdentifier ??= - options.Implementation is not null - ? $"{options.Implementation}.{method.RawName}" - : method.RawName; - var (suffix, extendingType) = options.IsWebAssembly - ? ("", "IJSInProcessRuntime") - : ("Async", "IJSRuntime"); + var isPrimitiveType = Primitives.IsPrimitiveType(method.RawReturnTypeName); + var containsGenericParameters = method.ParameterDefinitions.Any(p => p.IsGenericParameter(method.RawName, options)); - if (method.IsJavaScriptOverride(options) && options.Implementation is not null) - { - var impl = - options.Implementation.Substring( - options.Implementation.LastIndexOf(".") + 1); - - fullyQualifiedJavaScriptIdentifier = - $"blazorators.{impl}.{method.RawName}"; - - suffix = "Async"; - } - - var (returnType, bareType) = method.GetMethodTypes(options, isGenericReturnType, isPrimitiveType); + var genericTypeArgs = DetermineGenericTypeArgs(isGenericReturnType, containsGenericParameters); + var fullyQualifiedJavaScriptIdentifier = DetermineJavaScriptIdentifier(method, options); + (var suffix, var extendingType) = DetermineSuffixAndExtendingType(method, options); + (var returnType, var bareType) = method.GetMethodTypes(options, isGenericReturnType, isPrimitiveType); return new MethodBuilderDetails( Method: method, IsVoid: method.IsVoid, - IsPrimitiveType: TypeMap.PrimitiveTypes.IsPrimitiveType(method.RawReturnTypeName), + IsPrimitiveType: isPrimitiveType, IsGenericReturnType: isGenericReturnType, ContainsGenericParameters: containsGenericParameters, CSharpMethodName: method.RawName.CapitalizeFirstLetter(), @@ -76,6 +83,34 @@ options.Implementation is not null BareType: bareType, Suffix: suffix, ExtendingType: extendingType, - GenericTypeArgs: genericTypeArgs); + GenericTypeArgs: genericTypeArgs + ); + } + + private static string? DetermineGenericTypeArgs(bool isGenericReturnType, bool containsGenericParameters) + { + if (isGenericReturnType) return ToGenericTypeArgument(GenericTypeValue); + if (containsGenericParameters) return ToGenericTypeArgument(GenericTypeValue); + return null; + } + + private static string DetermineJavaScriptIdentifier(CSharpMethod method, GeneratorOptions options) + { + if (method.IsJavaScriptOverride(options) && options.Implementation is not null) + { + var impl = options.Implementation.Substring(options.Implementation.LastIndexOf(".") + 1); + return $"blazorators.{impl}.{method.RawName}"; + } + return method.JavaScriptMethodDependency?.InvokableMethodName ?? + (options.Implementation is not null ? $"{options.Implementation}.{method.RawName}" : method.RawName); + } + + private static (string Suffix, string ExtendingType) DetermineSuffixAndExtendingType(CSharpMethod method, GeneratorOptions options) + { + if (method.IsJavaScriptOverride(options) && options.Implementation is not null) + { + return ("Async", "IJSRuntime"); + } + return options.IsWebAssembly ? ("", "IJSInProcessRuntime") : ("Async", "IJSRuntime"); } -} +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Builders/NodeToSourceBuilder.cs b/src/Blazor.SourceGenerators/Builders/NodeToSourceBuilder.cs index b8dce1bd..486f5d4c 100644 --- a/src/Blazor.SourceGenerators/Builders/NodeToSourceBuilder.cs +++ b/src/Blazor.SourceGenerators/Builders/NodeToSourceBuilder.cs @@ -1,7 +1,7 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. -using System.Diagnostics; +using Blazor.SourceGenerators.Options; using Blazor.SourceGenerators.TypeScript.Types; namespace Blazor.SourceGenerators.Builders; @@ -50,12 +50,12 @@ internal NodeToSourceBuilder ResetIndentiationTo(int level) private void IncreaseIndentationImpl(bool increaseIndentation = false) => AdjustIndentation(increaseIndentation ? IndentationAdjustment.Increase - : IndentationAdjustment.Noop); + : IndentationAdjustment.NoOp); private void DecreaseIndentationImpl(bool decreaseIndentation = false) => AdjustIndentation(decreaseIndentation ? IndentationAdjustment.Decrease - : IndentationAdjustment.Noop); + : IndentationAdjustment.NoOp); private void AdjustIndentation(IndentationAdjustment adjustment) => _indentation = adjustment switch diff --git a/src/Blazor.SourceGenerators/Builders/PropertyBuilderDetails.cs b/src/Blazor.SourceGenerators/Builders/PropertyBuilderDetails.cs index a8c4c48b..abaea92f 100644 --- a/src/Blazor.SourceGenerators/Builders/PropertyBuilderDetails.cs +++ b/src/Blazor.SourceGenerators/Builders/PropertyBuilderDetails.cs @@ -1,8 +1,13 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +using Blazor.SourceGenerators.Options; + namespace Blazor.SourceGenerators.Builders; +/// +/// Represents the details of a property builder, including the C# property, its name, the fully qualified JavaScript identifier, the return type, the bare type, the suffix, the extending type, and the generic type arguments. +/// internal readonly record struct PropertyBuilderDetails( CSharpProperty Property, string CSharpPropertyName, @@ -13,25 +18,46 @@ internal readonly record struct PropertyBuilderDetails( string ExtendingType, string GenericTypeArgs) { + /// + /// Creates a new instance of based on the provided and . + /// + /// The to use for creating the . + /// The to use for creating the . + /// A new instance of based on the provided and . internal static PropertyBuilderDetails Create(CSharpProperty property, GeneratorOptions options) { var csharpPropertyName = property.RawName.CapitalizeFirstLetter(); - var javaScriptIndentifier = options.Implementation is not null - ? $"{options.Implementation}.{property.RawName}" - : property.RawName; + var fullyQualifiedJavaScriptIdentifier = DetermineJavaScriptIdentifier(property, options); var (returnType, bareType) = property.GetPropertyTypes(options); - var (suffix, extendingType) = - options.IsWebAssembly ? ("", "IJSInProcessRuntime") : ("Async", "IJSRuntime"); - var genericTypeArgs = $"<{bareType}>"; + var (suffix, extendingType) = DetermineSuffixAndExtendingType(options); + var genericTypeArgs = DetermineGenericTypeArgs(bareType); return new PropertyBuilderDetails( Property: property, CSharpPropertyName: csharpPropertyName, - FullyQualifiedJavaScriptIdentifier: javaScriptIndentifier, + FullyQualifiedJavaScriptIdentifier: fullyQualifiedJavaScriptIdentifier, ReturnType: returnType, BareType: bareType, Suffix: suffix, ExtendingType: extendingType, - GenericTypeArgs: genericTypeArgs); + GenericTypeArgs: genericTypeArgs + ); + } + + private static string DetermineJavaScriptIdentifier(CSharpProperty property, GeneratorOptions options) + { + return options.Implementation is not null + ? $"{options.Implementation}.{property.RawName}" + : property.RawName; + } + + private static (string Suffix, string ExtendingType) DetermineSuffixAndExtendingType(GeneratorOptions options) + { + return options.IsWebAssembly ? ("", "IJSInProcessRuntime") : ("Async", "IJSRuntime"); + } + + private static string DetermineGenericTypeArgs(string bareType) + { + return $"<{bareType}>"; } } diff --git a/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs b/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs index a6e78c24..dd13cfa1 100644 --- a/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs +++ b/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs @@ -1,15 +1,18 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. -using System.Diagnostics; +using Blazor.SourceGenerators.Options; namespace Blazor.SourceGenerators.Builders; +/// +/// Represents a builder for generating C# source code. +/// [DebuggerDisplay("{ToSourceCodeString()}", Name = "{_options.TypeName}")] internal sealed class SourceBuilder { - private const string _newLine = "\r\n"; - private const string _twoNewLines = $"{_newLine}{_newLine}"; + internal const char NewLine = '\n'; + private const string _twoNewLines = "\n\n"; private readonly StringBuilder _builder = new(); private readonly GeneratorOptions _options; @@ -19,24 +22,53 @@ internal sealed class SourceBuilder private string? _implementationName; private string? _interfaceName; + /// + /// Gets or sets the set of fields used by the source builder. + /// internal ISet? Fields { get; private set; } + + /// + /// Gets or sets the set of methods used by the source builder. + /// internal ISet? Methods { get; private set; } + /// + /// Gets the current indentation level of the source builder. + /// internal int IndentationLevel => _indentation.Level; + + /// + /// Gets the implementation name, which is lazily initialized the first time it is accessed. + /// internal string ImplementationName => _implementationName ??= - $"{_options.Implementation.ToImplementationName(_isService)}"; + _options.Implementation.ToImplementationName(_isService); + + /// + /// Gets the interface name, which is lazily initialized the first time it is accessed. + /// internal string InterfaceName => _interfaceName ??= _options.Implementation.ToInterfaceName(_isService); - internal SourceBuilder(GeneratorOptions options, bool isService = true) => - (_options, _isService) = (options, isService); + internal SourceBuilder(GeneratorOptions options, bool isService = true) + { + _options = options; + _isService = isService; + } + /// + /// Appends the copy right header to the source builder. + /// + /// The updated source builder. internal SourceBuilder AppendCopyRightHeader() { - _builder.Append($"// Copyright (c) David Pine. All rights reserved.{_newLine}"); - _builder.Append($"// Licensed under the MIT License:{_newLine}"); - _builder.Append($"// https://bit.ly/blazorators-license{_newLine}"); - _builder.Append($"// Auto-generated by blazorators.{_twoNewLines}"); + _builder.Append(""" + // Copyright (c) David Pine. All rights reserved. + // Licensed under the MIT License: + // https://bit.ly/blazorators-license + // Auto-generated by blazorators. + + + """); return this; } @@ -45,16 +77,16 @@ internal SourceBuilder AppendUsingDeclarations() { if (_options is { SupportsGenerics: true }) { - _builder.Append($"using Blazor.Serialization.Extensions;{_newLine}"); - _builder.Append($"using System.Text.Json;{_newLine}"); + _builder.Append($"using Blazor.Serialization.Extensions;{NewLine}"); + _builder.Append($"using System.Text.Json;{NewLine}"); } if (!_options.IsWebAssembly) { - _builder.Append($"using System.Threading.Tasks;{_newLine}"); + _builder.Append($"using System.Threading.Tasks;{NewLine}"); } - _builder.Append(_newLine); + _builder.Append(NewLine); return this; } @@ -63,7 +95,7 @@ internal SourceBuilder AppendNamespace(string namespaceString, bool isNullableCo { if (isNullableContext) { - _builder.Append($"#nullable enable{_newLine}"); + _builder.Append($"#nullable enable{NewLine}"); } _builder.Append($"namespace {namespaceString};{_twoNewLines}"); @@ -73,18 +105,18 @@ internal SourceBuilder AppendNamespace(string namespaceString, bool isNullableCo internal SourceBuilder AppendPublicInterfaceDeclaration() { - _builder.Append($"/// {_newLine}"); - _builder.Append($"/// Source generated interface definition of the {_options.TypeName} type.{_newLine}"); - _builder.Append($"/// {_newLine}"); - _builder.Append($"public partial interface {InterfaceName}{_newLine}"); + _builder.Append($"/// {NewLine}"); + _builder.Append($"/// Source generated interface definition of the {_options.TypeName} type.{NewLine}"); + _builder.Append($"/// {NewLine}"); + _builder.Append($"public partial interface {InterfaceName}{NewLine}"); return this; } internal SourceBuilder AppendInternalImplementationDeclaration() { - _builder.Append($"/// {_newLine}"); - _builder.Append($"internal sealed class {ImplementationName} : {InterfaceName}{_newLine}"); + _builder.Append($"/// {NewLine}"); + _builder.Append($"internal sealed class {ImplementationName} : {InterfaceName}{NewLine}"); return this; } @@ -96,7 +128,7 @@ internal SourceBuilder AppendImplementationCtor() : "IJSRuntime"; _builder.Append($"{_indentation}internal readonly {javaScriptRuntime} _javaScript = null!;{_twoNewLines}"); - _builder.Append($"{_indentation}public {ImplementationName}({javaScriptRuntime} javaScript) =>{_newLine}"); + _builder.Append($"{_indentation}public {ImplementationName}({javaScriptRuntime} javaScript) =>{NewLine}"); IncreaseIndentation(); @@ -111,7 +143,7 @@ internal SourceBuilder AppendOpeningCurlyBrace(bool increaseIndentation = false) { IncreaseIndentationImpl(increaseIndentation); - _builder.Append($"{_indentation}{{{_newLine}"); + _builder.Append($"{_indentation}{{{NewLine}"); return this; } @@ -120,7 +152,7 @@ internal SourceBuilder AppendClosingCurlyBrace(bool decreaseIndentation = false) { DecreaseIndentationImpl(decreaseIndentation); - _builder.Append($"{_indentation}}}{_newLine}"); + _builder.Append($"{_indentation}}}{NewLine}"); return this; } @@ -128,21 +160,20 @@ internal SourceBuilder AppendClosingCurlyBrace(bool decreaseIndentation = false) internal SourceBuilder AppendTripleSlashMethodComments( CSharpMethod method, bool extrapolateParameters = false, - IndentationAdjustment adjustment = IndentationAdjustment.Noop) + IndentationAdjustment adjustment = IndentationAdjustment.NoOp) { AdjustIndentation(adjustment); var indent = _indentation.ToString(); - _builder.Append($"{indent}/// {_newLine}"); + _builder.Append($"{indent}/// {NewLine}"); var jsMethodName = method.RawName.LowerCaseFirstLetter(); var func = $"{_options.Implementation}.{jsMethodName}"; - _builder.Append($"{indent}/// Source generated implementation of {func}.{_newLine}"); - var rootUrl = "https://developer.mozilla.org/docs/Web/API"; - var fullUrl = $"{rootUrl}/{_options.TypeName}/{jsMethodName}"; - _builder.Append($"{indent}/// {_newLine}"); - _builder.Append($"{indent}/// {_newLine}"); + _builder.Append($"{indent}/// Source generated implementation of {func}.{NewLine}"); + var fullUrl = $"https://developer.mozilla.org/docs/Web/API/{_options.TypeName}/{jsMethodName}"; + _builder.Append($"{indent}/// {NewLine}"); + _builder.Append($"{indent}/// {NewLine}"); if (extrapolateParameters) { @@ -151,7 +182,7 @@ internal SourceBuilder AppendTripleSlashMethodComments( if (index.IsFirst) { _builder.Append( - $"/// The calling Razor (or Blazor) component.{_newLine}"); + $"/// The calling Razor (or Blazor) component.{NewLine}"); } if (param.ActionDeclation is not null) @@ -162,12 +193,12 @@ internal SourceBuilder AppendTripleSlashMethodComments( $"Expects the name of a \"JSInvokableAttribute\" C# method with the following " + $"System.Action{{{string.Join(", ", dependentTypes)}}}\"."; _builder.Append( - $"/// {action}{_newLine}"); + $"/// {action}{NewLine}"); } else { _builder.Append( - $"/// The {param.RawTypeName} value.{_newLine}"); + $"/// The {param.RawTypeName} value.{NewLine}"); } } } @@ -176,12 +207,12 @@ internal SourceBuilder AppendTripleSlashMethodComments( } internal SourceBuilder AppendEmptyTripleSlashInheritdocComments( - IndentationAdjustment adjustment = IndentationAdjustment.Noop) + IndentationAdjustment adjustment = IndentationAdjustment.NoOp) { AdjustIndentation(adjustment); var indent = _indentation.ToString(); - _builder.Append($"{indent}/// {_newLine}"); + _builder.Append($"{indent}/// {NewLine}"); return this; } @@ -189,31 +220,30 @@ internal SourceBuilder AppendEmptyTripleSlashInheritdocComments( internal SourceBuilder AppendTripleSlashInheritdocComments( string csharpTypeName, string memberName, - IndentationAdjustment adjustment = IndentationAdjustment.Noop) + IndentationAdjustment adjustment = IndentationAdjustment.NoOp) { AdjustIndentation(adjustment); var indent = _indentation.ToString(); - _builder.Append($"{indent}/// {_newLine}"); + _builder.Append($"{indent}/// {NewLine}"); return this; } internal SourceBuilder AppendTripleSlashPropertyComments( CSharpProperty property, - IndentationAdjustment adjustment = IndentationAdjustment.Noop) + IndentationAdjustment adjustment = IndentationAdjustment.NoOp) { AdjustIndentation(adjustment); var indent = _indentation.ToString(); - _builder.Append($"{indent}/// {_newLine}"); + _builder.Append($"{indent}/// {NewLine}"); var jsMethodName = property.RawName.LowerCaseFirstLetter(); var func = $"{_options.Implementation}.{jsMethodName}"; _builder.Append($"{indent}/// Source generated implementation of {func}.\r\n"); - var rootUrl = "https://developer.mozilla.org/docs/Web/API"; - var fullUrl = $"{rootUrl}/{_options.TypeName}/{jsMethodName}"; + var fullUrl = $"https://developer.mozilla.org/docs/Web/API/{_options.TypeName}/{jsMethodName}"; _builder.Append($"{indent}/// \r\n"); _builder.Append($"{indent}/// \r\n"); @@ -225,7 +255,7 @@ internal SourceBuilder AppendLine() // We use a hard-coded new line instead of: // _builder.AppendLine() as the new line value changes by environment. // For consistency, we'll always generate the exact same new line. - _builder.Append(_newLine); + _builder.Append(NewLine); return this; } @@ -237,7 +267,7 @@ internal SourceBuilder AppendRaw( bool omitIndentation = false) { var indentation = omitIndentation ? "" : _indentation.ToString(); - _builder.Append($"{indentation}{content}{(appendNewLine ? _newLine : string.Empty)}"); + _builder.Append($"{indentation}{content}{(appendNewLine ? NewLine : string.Empty)}"); if (postIncreaseIndentation) { @@ -311,7 +341,7 @@ internal SourceBuilder AppendConditionalDelegateCallbackMethods(List(); foreach (var (interation, p) in param.ActionDeclation!.ParameterDefinitions!.Select()) @@ -328,8 +358,6 @@ private SourceBuilder AppendParameters(CSharpType param, string fieldName) AppendRaw($"{fieldName}?.Invoke({string.Join(", ", args)});"); } } - - return this; } internal SourceBuilder DecreaseIndentation() @@ -349,20 +377,19 @@ internal SourceBuilder ResetIndentiationTo(int level) private void IncreaseIndentationImpl(bool increaseIndentation = false) => AdjustIndentation(increaseIndentation ? IndentationAdjustment.Increase - : IndentationAdjustment.Noop); + : IndentationAdjustment.NoOp); private void DecreaseIndentationImpl(bool decreaseIndentation = false) => AdjustIndentation(decreaseIndentation ? IndentationAdjustment.Decrease - : IndentationAdjustment.Noop); + : IndentationAdjustment.NoOp); - private void AdjustIndentation(IndentationAdjustment adjustment) => - _indentation = adjustment switch - { - IndentationAdjustment.Increase => _indentation.Increase(), - IndentationAdjustment.Decrease => _indentation.Decrease(), - _ => _indentation - }; + private void AdjustIndentation(IndentationAdjustment adjustment) => _indentation = adjustment switch + { + IndentationAdjustment.Increase => _indentation.Increase(), + IndentationAdjustment.Decrease => _indentation.Decrease(), + _ => _indentation + }; internal string ToSourceCodeString() => _builder.ToString(); } diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpAction.cs b/src/Blazor.SourceGenerators/CSharp/CSharpAction.cs index b6dffa6e..26726e3e 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpAction.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpAction.cs @@ -23,7 +23,7 @@ namespace Blazor.SourceGenerators.CSharp; internal record CSharpAction( string RawName, string? RawReturnTypeName = "void", - List? ParameterDefinitions = null) : ICSharpDependencyGraphObject + IList? ParameterDefinitions = null) : ICSharpDependencyGraphObject { /// /// The collection of types that this object depends on. diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs b/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs index 96cf1523..13b9e8ae 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs @@ -6,7 +6,7 @@ namespace Blazor.SourceGenerators.CSharp; internal record CSharpMethod( string RawName, string RawReturnTypeName, - List ParameterDefinitions, + IList ParameterDefinitions, JavaScriptMethod? JavaScriptMethodDependency = null) : ICSharpDependencyGraphObject { public bool IsPureJavaScriptInvocation => @@ -20,8 +20,7 @@ internal record CSharpMethod( public bool IsVoid => RawReturnTypeName is "void"; - public Dictionary DependentTypes { get; init; } - = new(StringComparer.OrdinalIgnoreCase); + public Dictionary DependentTypes { get; init; } = new(StringComparer.OrdinalIgnoreCase); public IImmutableSet<(string TypeName, CSharpObject Object)> AllDependentTypes { diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpObject.cs b/src/Blazor.SourceGenerators/CSharp/CSharpObject.cs index b33001b8..b84f92d7 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpObject.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpObject.cs @@ -21,14 +21,13 @@ internal record CSharpObject( get { Dictionary result = new(StringComparer.OrdinalIgnoreCase); - foreach (var prop - in this.GetAllDependencies() - .Concat(Properties.SelectMany( - p => p.Value.AllDependentTypes)) - .Concat(Methods.SelectMany( - p => p.Value.AllDependentTypes))) + var members = this.GetAllDependencies() + .Concat(Properties.SelectMany(p => p.Value.AllDependentTypes)) + .Concat(Methods.SelectMany(p => p.Value.AllDependentTypes)); + + foreach (var member in members) { - result[prop.TypeName] = prop.Object; + result[member.TypeName] = member.Object; } return result.Select(kvp => (kvp.Key, kvp.Value)) @@ -54,23 +53,18 @@ in this.GetAllDependencies() public bool IsActionParameter => TypeName.EndsWith("Callback"); - internal string ToClassString() + public override string ToString() { StringBuilder builder = new("#nullable enable\r\n"); builder.Append("using System.Text.Json.Serialization;\r\n\r\n"); builder.Append("namespace Microsoft.JSInterop;\r\n\r\n"); - builder.Append( - $"/// \r\n"); - builder.Append( - $"/// Source-generated object representing an ideally immutable {TypeName} value.\r\n"); - builder.Append( - $"/// \r\n"); - + builder.Append($"/// \r\n"); + builder.Append($"/// Source-generated object representing an ideally immutable {TypeName} value.\r\n"); + builder.Append($"/// \r\n"); builder.Append($"public class {TypeName}\r\n{{\r\n"); - var memberCount = Properties.Count; foreach (var (index, kvp) in Properties.Select((kvp, index) => (index, kvp))) { @@ -78,41 +72,29 @@ in Properties.Select((kvp, index) => (index, kvp))) var typeName = member.MappedTypeName; var nullableExpression = member.IsNullable && !typeName.EndsWith("?") ? "?" : ""; var trivia = member.IsArray ? "[]" : ""; - var isPrimitive = TypeMap.PrimitiveTypes.IsPrimitiveType(typeName); + var isPrimitive = Primitives.IsPrimitiveType(typeName); var statementTerminator = member.IsNullable || - typeName is "string" || isPrimitive is false ? " = default!;" : ""; + typeName is "string" || !isPrimitive ? " = default!;" : ""; var csharpMemberName = memberName.CapitalizeFirstLetter(); - builder.Append( - $" /// \r\n"); - builder.Append( - $" /// Source-generated property representing the {TypeName}.{memberName} value.\r\n"); - builder.Append( - $" /// \r\n"); - builder.Append( - $" [JsonPropertyName(\"{memberName}\")]\r\n"); - builder.Append( - $" public {typeName}{trivia}{nullableExpression} {csharpMemberName} {{ get; set; }}{statementTerminator}\r\n"); + builder.Append($" /// \r\n"); + builder.Append($" /// Source-generated property representing the {TypeName}.{memberName} value.\r\n"); + builder.Append($" /// \r\n"); + builder.Append($" [JsonPropertyName(\"{memberName}\")]\r\n"); + builder.Append($" public {typeName}{trivia}{nullableExpression} {csharpMemberName} {{ get; set; }}{statementTerminator}\r\n"); // Add readonly property for converting DOMTimeStamp (long) to DateTime. if (member.RawTypeName is "DOMTimeStamp" or "DOMTimeStamp | null" or "EpochTimeStamp" or "EpochTimeStamp | null") { - builder.Append( - $" /// \r\n"); - builder.Append( - $" /// Source-generated property representing the {TypeName}.{memberName} value, \r\n"); - - builder.Append( - $" /// converted as a in UTC.\r\n"); - builder.Append( - $" /// \r\n"); + builder.Append($" /// \r\n"); + builder.Append($" /// Source-generated property representing the {TypeName}.{memberName} value, \r\n"); + builder.Append($" /// converted as a in UTC.\r\n"); + builder.Append($" /// \r\n"); var nullable = member.IsNullable ? "?" : ""; - builder.Append( - $" [JsonIgnore]\r\n"); - builder.Append( - $" public DateTime{nullable} {csharpMemberName}AsUtcDateTime => {csharpMemberName}.ToDateTimeFromUnix();\r\n"); + builder.Append($" [JsonIgnore]\r\n"); + builder.Append($" public DateTime{nullable} {csharpMemberName}AsUtcDateTime => {csharpMemberName}.ToDateTimeFromUnix();\r\n"); } } @@ -120,6 +102,4 @@ in Properties.Select((kvp, index) => (index, kvp))) var result = builder.ToString(); return result; } - - public override string ToString() => ToClassString(); -} +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpProperty.cs b/src/Blazor.SourceGenerators/CSharp/CSharpProperty.cs index fda29cb8..ddbbe0d8 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpProperty.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpProperty.cs @@ -4,7 +4,7 @@ namespace Blazor.SourceGenerators.CSharp; /// -/// A record the represents various C# members, such as properties, delegates and events. +/// A record that represents various C# members, such as properties, delegates, and events. /// internal record CSharpProperty( string RawName, @@ -12,34 +12,38 @@ internal record CSharpProperty( bool IsNullable = false, bool IsReadonly = false) : CSharpType(RawName, RawTypeName, IsNullable) { - public string MappedTypeName + /// + /// Gets the mapped type name, resolving primitive types and handling arrays and nullability. + /// + public string MappedTypeName => GetMappedTypeName(); + + /// + /// Determines if the property is an indexer. + /// + public bool IsIndexer => RawName.StartsWith("[") && RawName.EndsWith("]"); + + /// + /// Determines if the property is an array. + /// + public bool IsArray => RawTypeName.EndsWith("[]") || (RawTypeName.StartsWith("ReadonlyArray<") && RawTypeName.EndsWith(">")); + + private string GetMappedTypeName() { - get + var mappedTypeName = Primitives.Instance[RawTypeName]; + + if (IsArray) { - var mappedTypeName = TypeMap.PrimitiveTypes[RawTypeName]; - if (mappedTypeName == RawTypeName) - { - if (IsArray) - { - mappedTypeName = mappedTypeName - .Replace("[]", "") - .Replace("ReadonlyArray<", "") - .Replace(">", ""); - } - - if (IsNullable) - { - mappedTypeName = mappedTypeName - .Replace("| null", ""); - } - } - - return mappedTypeName; + mappedTypeName = mappedTypeName + .Replace("[]", "") + .Replace("ReadonlyArray<", "") + .Replace(">", ""); } - } - public bool IsIndexer => RawName.StartsWith("[") && RawName.EndsWith("]"); + if (IsNullable) + { + mappedTypeName = mappedTypeName.Replace("| null", ""); + } - public bool IsArray => RawTypeName.EndsWith("[]") || - (RawTypeName.StartsWith("ReadonlyArray<") && RawTypeName.EndsWith(">")); -} + return mappedTypeName; + } +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs index 8590878b..3d579a30 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs @@ -2,15 +2,16 @@ // Licensed under the MIT License. using Blazor.SourceGenerators.Builders; +using Blazor.SourceGenerators.Options; namespace Blazor.SourceGenerators.CSharp; internal sealed partial record CSharpTopLevelObject(string RawTypeName) : ICSharpDependencyGraphObject { - public List? Properties { get; init; } = new(); + public List Properties { get; init; } = []; - public List? Methods { get; init; } = new(); + public List Methods { get; init; } = []; public Dictionary DependentTypes { get; init; } = new(StringComparer.OrdinalIgnoreCase); @@ -37,9 +38,7 @@ in DependentTypes public int MemberCount => Properties!.Count + Methods!.Count; - internal string ToInterfaceString( - GeneratorOptions options, - string? namespaceString) + internal string ToInterfaceString(GeneratorOptions options, string? namespaceString) { var builder = new SourceBuilder(options) .AppendCopyRightHeader() @@ -52,7 +51,7 @@ internal string ToInterfaceString( var methodLevel = builder.IndentationLevel; // Methods - foreach (var method in Methods ?? new List()) + foreach (var method in Methods ?? []) { var details = MethodBuilderDetails.Create(method, options); builder.ResetIndentiationTo(methodLevel); @@ -108,7 +107,7 @@ internal string ToInterfaceString( builder.AppendRaw(");", appendNewLine: true, omitIndentation: true); } } - else if (options.OnlyGeneratePureJS is false) + else if (!options.OnlyGeneratePureJS) { var genericTypeArgs = details.GenericTypeArgs ?? MethodBuilderDetails.ToGenericTypeArgument( @@ -330,7 +329,7 @@ internal string ToImplementationString( } } } - else if (options.OnlyGeneratePureJS is false) + else if (!options.OnlyGeneratePureJS) { var genericTypeArgs = details.GenericTypeArgs ?? MethodBuilderDetails.ToGenericTypeArgument( @@ -496,9 +495,7 @@ internal string ToImplementationString( return implementation; } - internal string ToServiceCollectionExtensions( - GeneratorOptions options, - string implementation) + internal static string ToServiceCollectionExtensions(GeneratorOptions options, string implementation) { var serviceLifetime = options.IsWebAssembly ? "Singleton" diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpType.cs b/src/Blazor.SourceGenerators/CSharp/CSharpType.cs index c922f133..c75840fc 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpType.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpType.cs @@ -16,8 +16,7 @@ public Dictionary DependentTypes get { Dictionary result = new(StringComparer.OrdinalIgnoreCase); - foreach (var prop in ActionDeclation?.DependentTypes - ?? Enumerable.Empty>()) + foreach (var prop in ActionDeclation?.DependentTypes ?? []) { result[prop.Key] = prop.Value; } @@ -60,11 +59,11 @@ public string ToParameterString(bool isGenericType = false, bool overrideNullabi } var isCallback = ActionDeclation is not null; - var typeName = TypeMap.PrimitiveTypes.IsPrimitiveType(RawTypeName) - ? TypeMap.PrimitiveTypes[RawTypeName] - : isCallback - ? "string" // When the action is a callback, we require `T` instance and callback names. - : RawTypeName; + string typeName; + + if (Primitives.IsPrimitiveType(RawTypeName)) typeName = Primitives.Instance[RawTypeName]; + else if (isCallback) typeName = "string"; // When the action is a callback, we require `T` instance and callback names. + else typeName = RawTypeName; var parameterName = ToArgumentString(); var parameterDefault = overrideNullability ? "" : " = null"; @@ -102,4 +101,4 @@ public string ToArgumentString(bool toJson = false, bool asDelegate = false) ? $"{parameterName}.ToJson(options)" : parameterName; } -} +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Diagnostics/Descriptors.cs b/src/Blazor.SourceGenerators/Diagnostics/Descriptors.cs index 77662365..293530e7 100644 --- a/src/Blazor.SourceGenerators/Diagnostics/Descriptors.cs +++ b/src/Blazor.SourceGenerators/Diagnostics/Descriptors.cs @@ -3,7 +3,7 @@ namespace Blazor.SourceGenerators.Diagnostics; -static class Descriptors +internal static class Descriptors { internal static readonly DiagnosticDescriptor TypeNameRequiredDiagnostic = new( "BR0001", @@ -22,17 +22,17 @@ static class Descriptors true); internal static readonly DiagnosticDescriptor UnableToParseGeneratorOptionsDiagnostic = new( - id: "BR0003", - title: "The GeneratorOptions required for source generation are unresolvable", - messageFormat: "JSAutoGenericInteropAttribute must provide the fully qualified 'Descriptors' type name.", - category: "Blazorators.JSAutoGenericInteropAttribute", - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true); + "BR0003", + "The GeneratorOptions required for source generation are unresolvable", + "JSAutoGenericInteropAttribute must provide the fully qualified 'Descriptors' type name", + "Blazorators.JSAutoGenericInteropAttribute", + DiagnosticSeverity.Error, + true); internal static readonly DiagnosticDescriptor MissingBlazorSerializationPackageReferenceDiagnostic = new( id: "BR0004", title: "Missing package reference of Blazor.Serialization", - messageFormat: "When using JSAutoGenericInteropAttribute you must reference Blazor.Serialization.", + messageFormat: "When using JSAutoGenericInteropAttribute you must reference Blazor.Serialization", category: "Blazorators.JSAutoGenericInteropAttribute", defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true); @@ -40,10 +40,8 @@ static class Descriptors internal static readonly DiagnosticDescriptor SourceGenerationFailedDiagnostic = new( id: "BR0005", title: "Unknown error, send help?!", - messageFormat: """ - Try deleting your bin and obj folders, clean and try the build again. Exception: {0} - """, + messageFormat: "Try deleting your bin and obj folders, clean and try the build again. Exception: {0}.", category: "Blazor.SourceGenerators.JavaScriptInteropGenerator.TryExecute", defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); -} +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Expressions/SharedRegex.cs b/src/Blazor.SourceGenerators/Expressions/SharedRegex.cs deleted file mode 100644 index 17597af7..00000000 --- a/src/Blazor.SourceGenerators/Expressions/SharedRegex.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) David Pine. All rights reserved. -// Licensed under the MIT License. - -namespace Blazor.SourceGenerators.Expressions; - -internal static class SharedRegex -{ - // See: https://regex101.com/r/GV3DiG/1 - public static readonly Regex InterfaceRegex = - new("^(?'declaration'interface.*?{.*?})$", - RegexOptions.Singleline | RegexOptions.Multiline); - - public static readonly Regex InterfaceTypeNameRegex = - new("(?:interface )(?'TypeName'\\S+)"); - - public static readonly Regex ExtendsTypeNameRegex = - new("(?:extends )(?'TypeName'\\S+)"); - - public static readonly Regex TypeRegex = - new("^(?'type'type.*?)$", - RegexOptions.Singleline | RegexOptions.Multiline); - - public static readonly Regex TypeNameRegex = - new("(?:type )(?'TypeName'\\S+)"); - - /// - /// Given a string value of "clearWatch(watchId: number): void;", the - /// following capture groups would be present: - /// - /// MethodName: "clearWatch" - /// Parameters: "(watchId: number)" - /// ReturnType: ": void;" - /// - /// - public static readonly Regex TypeScriptMethodRegex = - new(@"^(?'MethodName'\S+(?=\())(?'Parameters'.*\))(?'ReturnType'\:.*)$", RegexOptions.Multiline); - - /// - /// Given a string value of "(position: GeolocationPosition): void;", the - /// following capture groups would be present: - /// - /// Parameters: "(position: GeolocationPosition" - /// ReturnType: ": void;" - /// - /// - public static readonly Regex TypeScriptCallbackRegex = - new(@"^(?'Parameters'\(.*\))(?'ReturnType'\:.*)$", RegexOptions.Multiline); - - public static readonly Regex TypeScriptPropertyRegex = - new(@"^(?'Name'.*)\:(?:.{1})(?'Type'.*)\;$", RegexOptions.Multiline); - - public static readonly Regex ArrayValuesRegex = - new(@"\[(?'Values'[^[\]]*)\]", RegexOptions.Multiline); -} diff --git a/src/Blazor.SourceGenerators/Extensions/AttributeSyntaxExtensions.cs b/src/Blazor.SourceGenerators/Extensions/AttributeSyntaxExtensions.cs index be04fe0f..453d887b 100644 --- a/src/Blazor.SourceGenerators/Extensions/AttributeSyntaxExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/AttributeSyntaxExtensions.cs @@ -1,9 +1,11 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +using Blazor.SourceGenerators.Options; + namespace Blazor.SourceGenerators.Extensions; -static class AttributeSyntaxExtensions +internal static class AttributeSyntaxExtensions { internal static GeneratorOptions GetGeneratorOptions( this AttributeSyntax attribute, @@ -12,8 +14,6 @@ internal static GeneratorOptions GetGeneratorOptions( GeneratorOptions options = new(supportsGenerics); if (attribute is { ArgumentList: not null }) { - var removeQuotes = static string (string value) => value.Replace("\"", ""); - foreach (var arg in attribute.ArgumentList.Arguments) { var propName = arg.NameEquals?.Name?.ToString(); @@ -21,11 +21,11 @@ internal static GeneratorOptions GetGeneratorOptions( { nameof(options.TypeName) => options with { - TypeName = removeQuotes(arg.Expression.ToString()) + TypeName = RemoveQuotes(arg.Expression.ToString()) }, nameof(options.Implementation) => options with { - Implementation = removeQuotes(arg.Expression.ToString()) + Implementation = RemoveQuotes(arg.Expression.ToString()) }, nameof(options.OnlyGeneratePureJS) => options with { @@ -33,7 +33,7 @@ internal static GeneratorOptions GetGeneratorOptions( }, nameof(options.Url) => options with { - Url = removeQuotes(arg.Expression.ToString()) + Url = RemoveQuotes(arg.Expression.ToString()) }, "HostingModel" => options with { @@ -62,33 +62,36 @@ internal static GeneratorOptions GetGeneratorOptions( static string[]? ParseArray(string args) { + // Remove unwanted parts of the string var replacedArgs = args .Replace("new[]", "") .Replace("new []", "") .Replace("new string[]", "") .Replace("new string []", "") .Replace("{", "[") - .Replace("}", "]"); + .Replace("}", "]") + .Trim(); - var values = SharedRegex.ArrayValuesRegex - .GetMatchGroupValue(replacedArgs, "Values"); + // Find the first '[' and the last ']' to extract the array contents + var startIndex = replacedArgs.IndexOf('[') + 1; + var endIndex = replacedArgs.LastIndexOf(']'); - if (values is not null) + // Check if the brackets are correctly positioned + if (startIndex > 0 && endIndex > startIndex) { - var trimmed = values.Trim(); - var descriptors = trimmed.Split(','); + var values = replacedArgs.Substring(startIndex, endIndex - startIndex); + + // Split the values by commas + var descriptors = values.Split(','); return descriptors - .Select(descriptor => - { - descriptor = descriptor - .Replace("\"", "") - .Trim(); - return descriptor; - }) + .Select(descriptor => RemoveQuotes(descriptor).Trim()) .ToArray(); } return default; } -} + + + private static string RemoveQuotes(string value) => value.Replace("\"", ""); +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Extensions/CSharpDependencyGraphExtensions.cs b/src/Blazor.SourceGenerators/Extensions/CSharpDependencyGraphExtensions.cs index 8e4d2384..3b0c3d6b 100644 --- a/src/Blazor.SourceGenerators/Extensions/CSharpDependencyGraphExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/CSharpDependencyGraphExtensions.cs @@ -5,14 +5,12 @@ namespace Blazor.SourceGenerators.Extensions; internal static class CSharpDependencyGraphExtensions { - internal static IImmutableSet<(string TypeName, CSharpObject Object)> GetAllDependencies( - this ICSharpDependencyGraphObject dependencyGraphObject) + internal static IImmutableSet<(string TypeName, CSharpObject Object)> GetAllDependencies(this ICSharpDependencyGraphObject dependencyGraphObject) { Dictionary map = new(StringComparer.OrdinalIgnoreCase); - foreach (var kvp - in dependencyGraphObject.DependentTypes?.Flatten( - obj => obj.Value.DependentTypes) - ?? Enumerable.Empty>()) + var flattened = dependencyGraphObject.DependentTypes?.Flatten(obj => obj.Value.DependentTypes) ?? []; + + foreach (var kvp in flattened) { map[kvp.Key] = kvp.Value; } @@ -20,4 +18,4 @@ internal static class CSharpDependencyGraphExtensions return map.Select(kvp => (kvp.Key, kvp.Value)) .ToImmutableHashSet(); } -} +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Extensions/CSharpMethodExtensions.cs b/src/Blazor.SourceGenerators/Extensions/CSharpMethodExtensions.cs index e0113b9a..23a97fcd 100644 --- a/src/Blazor.SourceGenerators/Extensions/CSharpMethodExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/CSharpMethodExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using Blazor.SourceGenerators.Builders; +using Blazor.SourceGenerators.Options; namespace Blazor.SourceGenerators.Extensions; @@ -10,51 +11,45 @@ internal static class CSharpMethodExtensions internal static bool IsJavaScriptOverride(this CSharpMethod method, GeneratorOptions options) { var methodName = method.RawName.LowerCaseFirstLetter(); - return options?.PureJavaScriptOverrides - ?.Any(overriddenMethodName => overriddenMethodName == methodName) - ?? false; + return Array.Exists(options.PureJavaScriptOverrides ?? [], overriddenMethodName => overriddenMethodName == methodName); } internal static bool IsGenericReturnType(this CSharpMethod method, GeneratorOptions options) => - options.GenericMethodDescriptors - ?.Any(descriptor => + Array.Exists(options.GenericMethodDescriptors ?? [], descriptor => + { + // If the descriptor describes a parameter, it's not a generic return. + // TODO: consider APIs that might do this. + if (descriptor.Contains(":")) { - // If the descriptor describes a parameter, it's not a generic return. - // TODO: consider APIs that might do this. - if (descriptor.Contains(":")) - { - return false; - } + return false; + } - // If the descriptor is the method name - return descriptor == method.RawName; - }) - ?? false; + // If the descriptor is the method name + return descriptor == method.RawName; + }); internal static bool IsGenericParameter(string methodName, CSharpType parameter, GeneratorOptions options) => - options.GenericMethodDescriptors - ?.Any(descriptor => + Array.Exists(options.GenericMethodDescriptors ?? [], descriptor => + { + if (!descriptor.StartsWith(methodName)) { - if (!descriptor.StartsWith(methodName)) - { - return false; - } + return false; + } - if (descriptor.Contains(":")) - { - var nameParamPair = descriptor.Split(':'); - return nameParamPair[1].StartsWith(parameter.RawName); - } + if (descriptor.Contains(":")) + { + var nameParamPair = descriptor.Split(':'); + return nameParamPair[1].StartsWith(parameter.RawName); + } - return false; - }) - ?? false; + return false; + }); internal static (string ReturnType, string BareType) GetMethodTypes( this CSharpMethod method, GeneratorOptions options, bool isGenericReturnType, bool isPrimitiveType) { var primitiveType = isPrimitiveType - ? TypeMap.PrimitiveTypes[method.RawReturnTypeName] + ? Primitives.Instance[method.RawReturnTypeName] : method.RawReturnTypeName; if (!method.IsVoid && isGenericReturnType) @@ -70,23 +65,59 @@ internal static (string ReturnType, string BareType) GetMethodTypes( var isJavaScriptOverride = method.IsJavaScriptOverride(options); if (options.IsWebAssembly && !isJavaScriptOverride) { - var returnType = isPrimitiveType - ? primitiveType - : method.IsVoid - ? "void" - : method.RawReturnTypeName; + string returnType; + if (isPrimitiveType) + { + returnType = primitiveType; + } + else if (method.IsVoid) + { + returnType = "void"; + } + else + { + if (method.RawReturnTypeName.StartsWith("Promise<")) + { + var genericType = method.RawReturnTypeName.ExtractGenericType(); + returnType = Primitives.IsPrimitiveType(genericType) + ? $"ValueTask<{Primitives.Instance[genericType]}>" + : $"ValueTask<{genericType}>"; + } + else + { + returnType = method.RawReturnTypeName; + } + } return (returnType, primitiveType); } else { - var returnType = isPrimitiveType - ? $"ValueTask<{primitiveType}>" - : method.IsVoid - ? "ValueTask" - : $"ValueTask<{method.RawReturnTypeName}>"; + string returnType; + if (isPrimitiveType) + { + returnType = $"ValueTask<{primitiveType}>"; + } + else if (method.IsVoid) + { + returnType = "ValueTask"; + } + else + { + if (method.RawReturnTypeName.StartsWith("Promise<")) + { + var genericType = method.RawReturnTypeName.ExtractGenericType(); + returnType = Primitives.IsPrimitiveType(genericType) + ? $"ValueTask<{Primitives.Instance[genericType]}>" + : $"ValueTask<{genericType}>"; + } + else + { + returnType = $"ValueTask<{method.RawReturnTypeName}>"; + } + } return (returnType, primitiveType); } } -} +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Extensions/CSharpPropertyExtensions.cs b/src/Blazor.SourceGenerators/Extensions/CSharpPropertyExtensions.cs index 3eca2b29..1279f5be 100644 --- a/src/Blazor.SourceGenerators/Extensions/CSharpPropertyExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/CSharpPropertyExtensions.cs @@ -1,6 +1,8 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +using Blazor.SourceGenerators.Options; + namespace Blazor.SourceGenerators.Extensions; internal static class CSharpPropertyExtensions diff --git a/src/Blazor.SourceGenerators/Extensions/CSharpTypeExtensions.cs b/src/Blazor.SourceGenerators/Extensions/CSharpTypeExtensions.cs index 2461d8aa..fc6736a4 100644 --- a/src/Blazor.SourceGenerators/Extensions/CSharpTypeExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/CSharpTypeExtensions.cs @@ -1,26 +1,26 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +using Blazor.SourceGenerators.Options; + namespace Blazor.SourceGenerators.Extensions; internal static class CSharpTypeExtensions { internal static bool IsGenericParameter(this CSharpType parameter, string methodName, GeneratorOptions options) => - options.GenericMethodDescriptors - ?.Any(descriptor => + Array.Exists(options.GenericMethodDescriptors ?? [], descriptor => + { + if (!descriptor.StartsWith(methodName)) { - if (!descriptor.StartsWith(methodName)) - { - return false; - } + return false; + } - if (descriptor.Contains(":")) - { - var nameParamPair = descriptor.Split(':'); - return nameParamPair[1].StartsWith(parameter.RawName); - } + if (descriptor.Contains(":")) + { + var nameParamPair = descriptor.Split(':'); + return nameParamPair[1].StartsWith(parameter.RawName); + } - return false; - }) - ?? false; + return false; + }); } diff --git a/src/Blazor.SourceGenerators/Extensions/EnumerableExtensions.cs b/src/Blazor.SourceGenerators/Extensions/EnumerableExtensions.cs index f6a80c22..0fa82beb 100644 --- a/src/Blazor.SourceGenerators/Extensions/EnumerableExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/EnumerableExtensions.cs @@ -5,11 +5,6 @@ namespace Blazor.SourceGenerators.Extensions; internal static class EnumerableExtensions { - internal static IEnumerable Flatten( - this IEnumerable source, - Func> childSelector) => - source?.SelectMany( - child => childSelector(child).Flatten(childSelector)) - .Concat(source ?? Enumerable.Empty()) - ?? Enumerable.Empty(); + internal static IEnumerable Flatten(this IEnumerable source, Func> childSelector) => + source?.SelectMany(child => childSelector(child).Flatten(childSelector)).Concat(source) ?? []; } diff --git a/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs b/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs index b289efcd..1442e3b3 100644 --- a/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs @@ -1,17 +1,16 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +using Blazor.SourceGenerators.Options; + namespace Blazor.SourceGenerators.Extensions; internal static class GeneratorExecutionContextExtensions { - internal static GeneratorExecutionContext AddDependentTypesSource( - this GeneratorExecutionContext context, - CSharpTopLevelObject topLevelObject) + internal static GeneratorExecutionContext AddDependentTypesSource(this GeneratorExecutionContext context, CSharpTopLevelObject topLevelObject) { - foreach (var (type, dependentObj) in - topLevelObject.AllDependentTypes.Where( - t => !t.Object.IsActionParameter)) + var dependentTypes = topLevelObject.AllDependentTypes.Where(type => !type.Object.IsActionParameter); + foreach (var (type, dependentObj) in dependentTypes) { context.AddSource(type.ToGeneratedFileName(), SourceText.From(dependentObj.ToString(), @@ -31,9 +30,7 @@ internal static GeneratorExecutionContext AddInterfaceSource( context.AddSource( $"{@interface}".ToGeneratedFileName(), SourceText.From( - topLevelObject.ToInterfaceString( - options, - @namespace), + topLevelObject.ToInterfaceString(options, @namespace), Encoding.UTF8)); return context; @@ -49,9 +46,7 @@ internal static GeneratorExecutionContext AddImplementationSource( context.AddSource( $"{implementation}".ToGeneratedFileName(), SourceText.From( - topLevelObject.ToImplementationString( - options, - @namespace), + topLevelObject.ToImplementationString(options, @namespace), Encoding.UTF8)); return context; @@ -59,16 +54,14 @@ internal static GeneratorExecutionContext AddImplementationSource( internal static GeneratorExecutionContext AddDependencyInjectionExtensionsSource( this GeneratorExecutionContext context, - CSharpTopLevelObject topLevelObject, + CSharpTopLevelObject _, string implementation, GeneratorOptions options) { context.AddSource( $"{options.Implementation.ToImplementationName(false)}ServiceCollectionExtensions".ToGeneratedFileName(), SourceText.From( - topLevelObject.ToServiceCollectionExtensions( - options, - implementation), + CSharpTopLevelObject.ToServiceCollectionExtensions(options, implementation), Encoding.UTF8)); return context; diff --git a/src/Blazor.SourceGenerators/Extensions/ListExtensions.cs b/src/Blazor.SourceGenerators/Extensions/ListExtensions.cs index d162363c..e69e6078 100644 --- a/src/Blazor.SourceGenerators/Extensions/ListExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/ListExtensions.cs @@ -5,7 +5,7 @@ namespace Blazor.SourceGenerators.Extensions; static class ListExtensions { - internal static IEnumerable<(Interation Index, T Item)> Select(this List list) + internal static IEnumerable<(Interation Index, T Item)> Select(this IList list) { var count = list.Count; for (var i = 0; i < count; ++i) diff --git a/src/Blazor.SourceGenerators/Extensions/NodeExtensions.cs b/src/Blazor.SourceGenerators/Extensions/NodeExtensions.cs index 55df7fe9..b3ab4a76 100644 --- a/src/Blazor.SourceGenerators/Extensions/NodeExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/NodeExtensions.cs @@ -7,8 +7,7 @@ namespace Blazor.SourceGenerators.Extensions; public static class NodeExtensions { - public static IEnumerable GetDescendants( - this INode node, bool includeSelf = true) + public static IEnumerable GetDescendants(this INode node, bool includeSelf = true) { if (includeSelf) yield return node; @@ -28,8 +27,7 @@ public static IEnumerable GetAncestors(this INode node) } } - public static IEnumerable OfKind( - this IEnumerable nodes, TypeScriptSyntaxKind kind) + public static IEnumerable OfKind(this IEnumerable nodes, TypeScriptSyntaxKind kind) { foreach (var node in nodes) { @@ -37,8 +35,7 @@ public static IEnumerable OfKind( } } - public static IEnumerable OfKind( - this IEnumerable nodes, TypeScriptSyntaxKind kind) + public static IEnumerable OfKind(this IEnumerable nodes, TypeScriptSyntaxKind kind) { foreach (var node in nodes) { diff --git a/src/Blazor.SourceGenerators/Extensions/RegexExtensions.cs b/src/Blazor.SourceGenerators/Extensions/RegexExtensions.cs index 74a15e7d..dfc1803b 100644 --- a/src/Blazor.SourceGenerators/Extensions/RegexExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/RegexExtensions.cs @@ -5,17 +5,14 @@ namespace Blazor.SourceGenerators.Extensions; static class RegexExtensions { - internal static string? GetMatchGroupValue( - this Regex regex, string input, string groupName) => + internal static string? GetMatchGroupValue(this Regex regex, string input, string groupName) => regex.Match(input) is Match match ? match.GetGroupValue(groupName) : default; - internal static string? GetGroupValue( - this Match match, string groupName) => - match switch - { - { Success: true } => match.Groups?[groupName]?.Value, - _ => default - }; + internal static string? GetGroupValue(this Match match, string groupName) => match switch + { + { Success: true } => match.Groups?[groupName]?.Value, + _ => default + }; } diff --git a/src/Blazor.SourceGenerators/Extensions/StringExtensions.cs b/src/Blazor.SourceGenerators/Extensions/StringExtensions.cs index b52f7131..a4d7942d 100644 --- a/src/Blazor.SourceGenerators/Extensions/StringExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/StringExtensions.cs @@ -5,45 +5,24 @@ namespace Blazor.SourceGenerators.Extensions; -static class StringExtensions +internal static class StringExtensions { - internal static string CapitalizeFirstLetter(this string name) => - $"{char.ToUpper(name[0])}{name.Substring(1, name.Length - 1)}"; - - internal static string LowerCaseFirstLetter(this string name) => - $"{char.ToLower(name[0])}{name.Substring(1, name.Length - 1)}"; - - internal static string ToGeneratedFileName(this string name) => $"{name}.g.cs"; - - internal static string ToImplementationName(this string implementation, bool isService = true) - { - var impl = (implementation.Contains(".") - ? implementation.Substring(implementation.LastIndexOf(".") + 1) - : implementation).CapitalizeFirstLetter(); - - return $"{impl}{(isService ? "Service" : "")}"; - } - - internal static string ToInterfaceName(this string implementation, bool isService = true) - { - var type = (implementation.Contains(".") - ? implementation.Substring(implementation.LastIndexOf(".") + 1) - : implementation).CapitalizeFirstLetter(); - - return $"I{type}{(isService ? "Service" : "")}"; - } - public static CharacterCode CharCodeAt(this string str, int pos) => (CharacterCode)str[pos]; - public static string SubString(this string str, int start, int? end = null) => - end is null ? str.Substring(start) : str.Substring(start, (int)end - start); + public static string FromCharCode(params int[] codes) + { + var sb = new StringBuilder(); + foreach (var c in codes) + { + sb.Append((char)c); + } + return sb.ToString(); + } public static string[] Match(this Regex regex, string text) => regex.Match(text).Captures.Cast().ToArray(); - public static bool Test(this Regex r, string text) => r.IsMatch(text); - public static void Pop(this List list) => list.RemoveAt(0); public static string Slice(this string str, int start, int end = int.MaxValue) @@ -58,13 +37,47 @@ public static string Slice(this string str, int start, int end = int.MaxValue) return end <= start ? string.Empty : str.Substring(start, end - 1); } - public static string FromCharCode(params int[] codes) + public static string SubString(this string str, int start, int? end = null) => + end is null ? str.Substring(start) : str.Substring(start, (int)end - start); + + public static bool Test(this Regex r, string text) => r.IsMatch(text); + + internal static string CapitalizeFirstLetter(this string name) => + $"{char.ToUpper(name[0])}{name.Substring(1, name.Length - 1)}"; + + internal static string ExtractGenericType(this string rawTypeName) { - var sb = new StringBuilder(); - foreach (var c in codes) + var startIndex = rawTypeName.IndexOf('<') + 1; + var endIndex = rawTypeName.LastIndexOf('>'); + + if (startIndex < 0 || endIndex >= rawTypeName.Length || endIndex < startIndex) { - sb.Append((char)c); + return rawTypeName; } - return sb.ToString(); + + return rawTypeName.Substring(startIndex, endIndex - startIndex); + } + + internal static string LowerCaseFirstLetter(this string name) => + $"{char.ToLower(name[0])}{name.Substring(1, name.Length - 1)}"; + + internal static string ToGeneratedFileName(this string name) => $"{name}.g.cs"; + + internal static string ToImplementationName(this string implementation, bool isService = true) + { + var impl = (implementation.Contains(".") + ? implementation.Substring(implementation.LastIndexOf(".") + 1) + : implementation).CapitalizeFirstLetter(); + + return $"{impl}{(isService ? "Service" : "")}"; + } + + internal static string ToInterfaceName(this string implementation, bool isService = true) + { + var type = (implementation.Contains(".") + ? implementation.Substring(implementation.LastIndexOf(".") + 1) + : implementation).CapitalizeFirstLetter(); + + return $"I{type}{(isService ? "Service" : "")}"; } -} +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/GlobalUsings.cs b/src/Blazor.SourceGenerators/GlobalUsings.cs index 681d41a2..7326d40f 100644 --- a/src/Blazor.SourceGenerators/GlobalUsings.cs +++ b/src/Blazor.SourceGenerators/GlobalUsings.cs @@ -3,11 +3,11 @@ global using System.Collections.Concurrent; global using System.Collections.Immutable; +global using System.Diagnostics; global using System.Text; global using System.Text.RegularExpressions; global using Blazor.SourceGenerators.CSharp; global using Blazor.SourceGenerators.Diagnostics; -global using Blazor.SourceGenerators.Expressions; global using Blazor.SourceGenerators.Extensions; global using Blazor.SourceGenerators.JavaScript; global using Blazor.SourceGenerators.Parsers; @@ -17,5 +17,4 @@ global using Microsoft.CodeAnalysis.CSharp; global using Microsoft.CodeAnalysis.CSharp.Syntax; global using Microsoft.CodeAnalysis.Text; -global using static Blazor.SourceGenerators.Expressions.SharedRegex; global using static Blazor.SourceGenerators.Source.SourceCode; diff --git a/src/Blazor.SourceGenerators/InterfaceDeclarationDetails.cs b/src/Blazor.SourceGenerators/InterfaceDeclarationDetails.cs index 7c11f9e9..ac6cac7f 100644 --- a/src/Blazor.SourceGenerators/InterfaceDeclarationDetails.cs +++ b/src/Blazor.SourceGenerators/InterfaceDeclarationDetails.cs @@ -1,6 +1,8 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +using Blazor.SourceGenerators.Options; + namespace Blazor.SourceGenerators; record class InterfaceDeclarationDetails( diff --git a/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs b/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs index cbcce8f7..6d7ef6b7 100644 --- a/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs +++ b/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs @@ -1,31 +1,25 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +using Blazor.SourceGenerators.Options; + namespace Blazor.SourceGenerators; [Generator] internal sealed partial class JavaScriptInteropGenerator : ISourceGenerator { - private readonly HashSet<(string FileName, string SourceCode)> _sourceCodeToAdd = new() - { + private readonly HashSet<(string FileName, string SourceCode)> _sourceCodeToAdd = + [ (nameof(RecordCompat).ToGeneratedFileName(), RecordCompat), (nameof(BlazorHostingModel).ToGeneratedFileName(), BlazorHostingModel), (nameof(JSAutoInteropAttribute).ToGeneratedFileName(), JSAutoInteropAttribute), (nameof(JSAutoGenericInteropAttribute).ToGeneratedFileName(), JSAutoGenericInteropAttribute), - }; + ]; public void Initialize(GeneratorInitializationContext context) { -#if DEBUG - if (!System.Diagnostics.Debugger.IsAttached) - { - // System.Diagnostics.Debugger.Launch(); - } -#endif - // Register a syntax receiver that will be created for each generation pass - context.RegisterForSyntaxNotifications( - JavaScriptInteropSyntaxContextReceiver.Create); + context.RegisterForSyntaxNotifications(JavaScriptInteropSyntaxContextReceiver.Create); } public void Execute(GeneratorExecutionContext context) => TryExecute(context); @@ -37,8 +31,7 @@ private void TryExecute(GeneratorExecutionContext context) // Add source from text. foreach (var (fileName, sourceCode) in _sourceCodeToAdd) { - context.AddSource(fileName, - SourceText.From(sourceCode, Encoding.UTF8)); + context.AddSource(fileName, SourceText.From(sourceCode, Encoding.UTF8)); } if (context.SyntaxContextReceiver is not JavaScriptInteropSyntaxContextReceiver receiver) @@ -71,17 +64,15 @@ private void TryExecute(GeneratorExecutionContext context) var result = parser.ParseTargetType(options.TypeName!); if (result is { Status: ParserResultStatus.SuccessfullyParsed, Value: { } }) { - var namespaceString = - (typeSymbol.ContainingNamespace.ToDisplayString(), classDeclaration.Parent) switch - { - (string { Length: > 0 } containingNamespace, _) => containingNamespace, - (_, BaseNamespaceDeclarationSyntax namespaceDeclaration) => namespaceDeclaration.Name.ToString(), - _ => null - }; - var @interface = - options.Implementation.ToInterfaceName(); - var implementation = - options.Implementation.ToImplementationName(); + var namespaceString = (typeSymbol.ContainingNamespace.ToDisplayString(), classDeclaration.Parent) switch + { + (string { Length: > 0 } containingNamespace, _) => containingNamespace, + (_, BaseNamespaceDeclarationSyntax namespaceDeclaration) => namespaceDeclaration.Name.ToString(), + _ => null + }; + + var @interface = options.Implementation.ToInterfaceName(); + var implementation = options.Implementation.ToImplementationName(); var topLevelObject = result.Value; context.AddDependentTypesSource(topLevelObject) @@ -110,7 +101,7 @@ private void TryExecute(GeneratorExecutionContext context) } } - static bool IsDiagnosticError(GeneratorOptions options, GeneratorExecutionContext context, AttributeSyntax attribute) + private static bool IsDiagnosticError(GeneratorOptions options, GeneratorExecutionContext context, AttributeSyntax attribute) { if (options.TypeName is null) { @@ -133,8 +124,8 @@ static bool IsDiagnosticError(GeneratorOptions options, GeneratorExecutionContex } if (options.SupportsGenerics && - context.Compilation.ReferencedAssemblyNames.Any(ai => - ai.Name.Equals("Blazor.Serialization", StringComparison.OrdinalIgnoreCase)) is false) + !context.Compilation.ReferencedAssemblyNames.Any( + ai => ai.Name.Equals("Blazor.Serialization", StringComparison.OrdinalIgnoreCase))) { context.ReportDiagnostic( Diagnostic.Create( @@ -146,4 +137,4 @@ static bool IsDiagnosticError(GeneratorOptions options, GeneratorExecutionContex return false; } -} +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/JavaScriptInteropSyntaxContextReceiver.cs b/src/Blazor.SourceGenerators/JavaScriptInteropSyntaxContextReceiver.cs index b7f308ff..3f6ff7b4 100644 --- a/src/Blazor.SourceGenerators/JavaScriptInteropSyntaxContextReceiver.cs +++ b/src/Blazor.SourceGenerators/JavaScriptInteropSyntaxContextReceiver.cs @@ -7,7 +7,7 @@ internal class JavaScriptInteropSyntaxContextReceiver : ISyntaxContextReceiver { internal static ISyntaxContextReceiver Create() => new JavaScriptInteropSyntaxContextReceiver(); - public HashSet InterfaceDeclarations { get; } = new(); + public HashSet InterfaceDeclarations { get; } = []; public void OnVisitSyntaxNode(GeneratorSyntaxContext context) { diff --git a/src/Blazor.SourceGenerators/Options/GeneratorOptions.cs b/src/Blazor.SourceGenerators/Options/GeneratorOptions.cs index e658665b..228cca73 100644 --- a/src/Blazor.SourceGenerators/Options/GeneratorOptions.cs +++ b/src/Blazor.SourceGenerators/Options/GeneratorOptions.cs @@ -1,6 +1,8 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +namespace Blazor.SourceGenerators.Options; + /// /// The options used (and parsed from the JSAutoInteropAttribute) to source-generate JavaScript interop. /// @@ -28,7 +30,7 @@ internal sealed record GeneratorOptions( string[]? TypeDeclarationSources = null, bool IsWebAssembly = true) { - ISet? _parsers; + private readonly HashSet _parsers = []; /// /// Get instance maps its @@ -36,16 +38,17 @@ internal sealed record GeneratorOptions( /// When is null, or empty, /// the default lib.dom.d.ts parser is used. /// - internal ISet Parsers + internal HashSet Parsers { get { - _parsers ??= new HashSet(); + var sources = TypeDeclarationSources? + .Select(TypeDeclarationReader.Factory) + .Select(reader => new TypeDeclarationParser(reader)); + + sources ??= [TypeDeclarationParser.Default]; - foreach (var source in - TypeDeclarationSources?.Select(TypeDeclarationReader.Factory) - ?.Select(reader => new TypeDeclarationParser(reader)) - ?? new[] { TypeDeclarationParser.Default }) + foreach (var source in sources) { _parsers.Add(source); } @@ -53,4 +56,4 @@ internal ISet Parsers return _parsers; } } -} +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs index 1bd357cc..3b732d49 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs @@ -1,483 +1,277 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +using Blazor.SourceGenerators.TypeScript.Types; + namespace Blazor.SourceGenerators.Parsers; internal sealed partial class TypeDeclarationParser { - internal CSharpObject? ToObject(string typeScriptTypeDeclaration) + internal CSharpObject ToObject(string typeName) { - CSharpObject? cSharpObject = null; - - var lineTokens = typeScriptTypeDeclaration.Split(new[] { '\n' }); - foreach (var (index, segment) in lineTokens.Select((s, i) => (i, s))) + if (TryGetCustomType(typeName, out var typescriptInterface)) { - if (index == 0) - { - var typeName = InterfaceTypeNameRegex.GetMatchGroupValue(segment, "TypeName"); - - // Ignore event targets for now. - var seg = segment.Replace(" extends EventTarget", ""); - var subclass = ExtendsTypeNameRegex.GetMatchGroupValue(seg, "TypeName"); - if (typeName is not null) - { - cSharpObject = new(typeName, subclass); - continue; - } - else - { - break; - } - } - - if (cSharpObject is null) - { - break; - } + return ToObject(typescriptInterface); + } - var line = segment.Trim(); - if (line.Length == 0) - { - continue; - } + return default!; + } - if (line == "}") - { - // We're done - break; - } + internal CSharpObject ToObject(InterfaceDeclaration typescriptInterface) + { + var heritage = typescriptInterface.HeritageClauses? + .SelectMany(heritage => heritage.Types) + .Where(type => type.Identifier is not "EventTarget") + .Select(type => type.Identifier) + .ToArray(); - if (IsMethod(line, out var method) && method is not null) - { - var methodName = method.GetGroupValue("MethodName"); - var parameters = method.GetGroupValue("Parameters"); - var returnType = method.GetGroupValue("ReturnType"); + var subclass = heritage is null || heritage.Length == 0 ? "" : string.Join(", ", heritage); - if (methodName is null || parameters is null || returnType is null) - { - continue; - } + var csharpObject = new CSharpObject(typescriptInterface.Identifier, subclass); - var (parameterDefinitions, javaScriptMethod) = - ParseParameters( - cSharpObject.TypeName, - methodName, - parameters, - obj => cSharpObject.DependentTypes![obj.TypeName] = obj); + var objectMethods = typescriptInterface.OfKind(TypeScriptSyntaxKind.MethodSignature); + var methods = ParseMethods( + csharpObject.TypeName, + objectMethods, + (dependency) => csharpObject.DependentTypes[dependency.TypeName] = dependency); + csharpObject = csharpObject with + { + Methods = methods + .GroupBy(method => method.RawName) + .ToDictionary(method => method.Key, method => method.Last()) + }; - var cSharpMethod = - ToMethod(methodName, returnType, parameterDefinitions, javaScriptMethod); - cSharpObject.Methods[cSharpMethod.RawName] = cSharpMethod; + var objectProperties = typescriptInterface.OfKind(TypeScriptSyntaxKind.PropertySignature); + var properties = ParseProperties( + objectProperties, + (dependency) => csharpObject.DependentTypes[dependency.TypeName] = dependency); - continue; - } + csharpObject = csharpObject with + { + Properties = properties + .GroupBy(property => property.RawName) + .ToDictionary(property => property.Key, method => method.Last()) + }; - if (IsProperty(line, out var property) && property is not null) - { - var name = property.GetGroupValue("Name"); - var type = property.GetGroupValue("Type"); + return csharpObject; + } - if (name is null || type is null) - { - continue; - } + internal CSharpTopLevelObject ToTopLevelObject(string typeName) + { + if (TryGetCustomType(typeName, out var typescriptInterface)) + { + return ToTopLevelObject(typescriptInterface); + } - var isReadonly = name.StartsWith("readonly "); - var isNullable = name.EndsWith("?") || type.Contains("| null"); + return default!; + } - name = name.Replace("?", "").Replace("readonly ", ""); - type = TryGetPrimitiveType(type); + internal CSharpTopLevelObject ToTopLevelObject(InterfaceDeclaration typescriptInterface) + { + var csharpTopLevelObject = new CSharpTopLevelObject(typescriptInterface.Identifier); - CSharpProperty cSharpProperty = new(name, type, isNullable, isReadonly); - cSharpObject.Properties[cSharpProperty.RawName] = cSharpProperty; + var objectMethods = typescriptInterface.OfKind(TypeScriptSyntaxKind.MethodSignature); + var methods = ParseMethods( + csharpTopLevelObject.RawTypeName, + objectMethods, + (dependency) => csharpTopLevelObject.DependentTypes[dependency.TypeName] = dependency); - var mappedType = cSharpProperty.MappedTypeName; + csharpTopLevelObject.Methods.AddRange(methods); - // When a property defines a custom type, that type needs to also be parsed - // and source generated. This is so that dependent types are known and resolved. - if (!TypeMap.PrimitiveTypes.IsPrimitiveType(mappedType) && - _reader.TryGetDeclaration(mappedType, out var typeScriptDefinitionText) && - typeScriptDefinitionText is not null) - { - var obj = ToObject(typeScriptDefinitionText); - if (obj is not null) - { - cSharpObject.DependentTypes![obj.TypeName] = obj; - } - } + var objectProperties = typescriptInterface.OfKind(TypeScriptSyntaxKind.PropertySignature); + var properties = ParseProperties( + objectProperties, + (dependency) => csharpTopLevelObject.DependentTypes[dependency.TypeName] = dependency); - continue; - } - } + csharpTopLevelObject.Properties.AddRange(properties); - return cSharpObject; + return csharpTopLevelObject; } - internal CSharpTopLevelObject? ToTopLevelObject(string typeScriptTypeDeclaration) + private static string GetNodeText(INode propertyTypeNode) { - CSharpTopLevelObject? topLevelObject = null; + return propertyTypeNode.GetText().ToString().Trim(); + } - var lineTokens = typeScriptTypeDeclaration.Split(new[] { '\n' }); - foreach (var (index, segment) in lineTokens.Select((s, i) => (i, s))) + private IEnumerable ParseMethods(string rawTypeName, IEnumerable objectMethods, Action appendDependency) + { + ICollection methods = []; + foreach (var method in objectMethods.Cast()) { - if (index == 0) - { - var typeName = InterfaceTypeNameRegex.GetMatchGroupValue(segment, "TypeName"); - if (typeName is not null) - { - topLevelObject = new(typeName); - continue; - } - else - { - break; - } - } + var methodName = method.Identifier; + var methodParameters = method.Parameters; + var methodReturnType = GetNodeText(method.Type); - if (topLevelObject is null) - { - break; - } - - var line = segment.Trim(); - if (line.Length == 0) + if (methodName is null || methodParameters is null || string.IsNullOrEmpty(methodReturnType)) { continue; } - if (line == "}") - { - // We're done - break; - } + var (csharpParameters, javascriptMethod) = ParseParameters( + rawTypeName, + methodName, + methodParameters, + appendDependency); - if (IsMethod(line, out var method) && method is not null) - { - var methodName = method.GetGroupValue("MethodName"); - var parameters = method.GetGroupValue("Parameters"); - var returnType = method.GetGroupValue("ReturnType"); + var csharpMethod = ToMethod(methodName, methodReturnType, csharpParameters, javascriptMethod); - if (methodName is null || parameters is null || returnType is null) - { - continue; - } + methods.Add(csharpMethod); + } - var (parameterDefinitions, javaScriptMethod) = - ParseParameters( - topLevelObject.RawTypeName, - methodName, - parameters, - obj => topLevelObject.DependentTypes![obj.TypeName] = obj); + return methods; + } - var cSharpMethod = - ToMethod(methodName, returnType, parameterDefinitions, javaScriptMethod); + private (IList, JavaScriptMethod) ParseParameters(string rawTypeName, string methodName, NodeArray methodParameters, Action appendDependency) + { + IList parameters = []; + var javascriptMethod = new JavaScriptMethod(methodName); - topLevelObject.Methods!.Add(cSharpMethod); + foreach (var parameter in methodParameters) + { + var isNullable = parameter.QuestionToken is not null; + var parameterName = parameter.Identifier; - continue; - } + var parameterType = GetNodeText(parameter.Children[parameter.Children.Count - 1]); + parameterType = isNullable ? parameterType.Replace(" | null", "") : parameterType; - if (IsProperty(line, out var property) && property is not null) - { - var name = property.GetGroupValue("Name"); - var type = property.GetGroupValue("Type"); + CSharpAction csharpAction = null!; - if (name is null || type is null) + // When a parameter defines a custom type, that type needs to also be parsed + // and source generated. This is so that dependent types are known and resolved. + if (TryGetCustomType(parameterType, out var typescriptInterface)) + { + javascriptMethod = javascriptMethod with { - continue; - } - - var isReadonly = name.StartsWith("readonly "); - var isNullable = name.EndsWith("?") || type.Contains("| null"); - - name = name.Replace("?", "").Replace("readonly ", ""); - type = TryGetPrimitiveType(type); - - CSharpProperty cSharpProperty = - new(name, - type, - isNullable, - isReadonly); - - topLevelObject.Properties!.Add(cSharpProperty); - - var mappedType = cSharpProperty.MappedTypeName; + InvokableMethodName = $"blazorators.{rawTypeName.LowerCaseFirstLetter()}.{methodName}" + }; - // When a property defines a custom type, that type needs to also be parsed - // and source generated. This is so that dependent types are known and resolved. - if (!TypeMap.PrimitiveTypes.IsPrimitiveType(mappedType) && - _reader.TryGetDeclaration(mappedType, out var typeScriptDefinitionText) && - typeScriptDefinitionText is not null) + if (parameterName.EndsWith("Callback")) { - var obj = ToObject(typeScriptDefinitionText); - if (obj is not null) + csharpAction = ToAction(typescriptInterface); + javascriptMethod = javascriptMethod with { - topLevelObject.DependentTypes![obj.TypeName] = obj; - } + IsBiDirectionalJavaScript = true, + }; } - - continue; - } - } - - return topLevelObject; - } - - private string TryGetPrimitiveType(string type) - { - if (!TypeMap.PrimitiveTypes.IsPrimitiveType(type) && - _reader.TryGetTypeAlias(type, out var typeAliasLine) && - typeAliasLine is not null) - { - if (typeAliasLine.Replace(";", "").Split('=') - is { Length: 2 } split) - { - if (split[1].Trim().Split('|') - is { Length: > 0 } values && - values.Select(v => v.Trim())?.ToList() - is { Count: > 0 } list) + else { - var isStringAlias = list.All(v => v.StartsWith("\"") && v.EndsWith("\"")); - if (isStringAlias) + var csharpObject = ToObject(typescriptInterface); + if (csharpObject is not null) { - type = "string"; + appendDependency.Invoke(csharpObject); } } } + + parameters.Add(new CSharpType(parameterName, parameterType, isNullable, csharpAction)); } - return type; + return (parameters, javascriptMethod); } - private CSharpMethod ToMethod( - string methodName, - string returnType, - List parameterDefinitions, - JavaScriptMethod? javaScriptMethod) + private IEnumerable ParseProperties(IEnumerable objectProperties, Action appendDependency) { - var methodReturnType = CleanseReturnType(returnType); - CSharpMethod cSharpMethod = - new(methodName, - methodReturnType, - parameterDefinitions, - javaScriptMethod); - - var nonArrayMethodReturnType = methodReturnType.Replace("[]", ""); - if (!TypeMap.PrimitiveTypes.IsPrimitiveType(nonArrayMethodReturnType) && - _reader.TryGetDeclaration(nonArrayMethodReturnType, out var typeScriptDefinitionText) && - typeScriptDefinitionText is not null) + ICollection properties = []; + foreach (var property in objectProperties.Cast()) { - var dependentType = ToObject(typeScriptDefinitionText); - if (dependentType is not null) - { - cSharpMethod.DependentTypes![nonArrayMethodReturnType] = dependentType; - } - } + var isReadonly = property.Modifiers.Exists(modifier => modifier.Kind is TypeScriptSyntaxKind.ReadonlyKeyword); + var isNullable = property.QuestionToken is not null; - return cSharpMethod; - } + var propertyName = property.Identifier; - internal CSharpAction? ToAction(string typeScriptTypeDeclaration) - { - CSharpAction? cSharpAction = null; + var propertyTypeNode = property.Children[property.Children.Count - 1]; - var lineTokens = typeScriptTypeDeclaration.Split(new[] { '\n' }); - foreach (var (index, segment) in lineTokens.Select((s, i) => (i, s))) - { - if (index == 0) - { - var typeName = InterfaceTypeNameRegex.GetMatchGroupValue(segment, "TypeName"); - if (typeName is not null) - { - cSharpAction = new(typeName); - continue; - } - else - { - break; - } - } + // TODO: Handle other type of nodes correctly + // Examples: + // - SomeCustomType | null #UnionNodeType + // - ((this: SomeCustom, ev: Event) => any) #ParenthesizedTypeNode, inside #FunctionTypeNode - if (cSharpAction is null) + var propertyType = propertyTypeNode switch { - break; - } + _ when isNullable => GetNodeText(propertyTypeNode).Replace(" | null", ""), + _ => GetNodeText(propertyTypeNode) + }; - var line = segment.Trim(); - if (line.Length == 0) + if (propertyName is null || string.IsNullOrEmpty(propertyType)) { continue; } - if (line == "}") - { - // We're done - break; - } + var csharpProperty = new CSharpProperty(propertyName, propertyType, isNullable, isReadonly); + properties.Add(csharpProperty); - if (IsAction(line, out var action) && action is not null) - { - var parameters = action.GetGroupValue("Parameters"); - var returnType = action.GetGroupValue("ReturnType"); + var mappedType = csharpProperty.MappedTypeName; - if (parameters is null || returnType is null) + // When a property defines a custom type, that type needs to also be parsed + // and source generated. This is so that dependent types are known and resolved. + if (TryGetCustomType(mappedType, out var typescriptInterface)) + { + var csharpObject = ToObject(typescriptInterface); + if (csharpObject is not null) { - continue; + appendDependency.Invoke(csharpObject); } - - var (parameterDefinitions, _) = - ParseParameters( - cSharpAction.RawName, - cSharpAction.RawName, - parameters, - obj => cSharpAction.DependentTypes![obj.TypeName] = obj); - - cSharpAction = cSharpAction with - { - ParameterDefinitions = parameterDefinitions - }; - - continue; } } - return cSharpAction; + return properties; } - internal static string CleanseReturnType(string returnType) => - // Example inputs: - // 1) ": void;" - // 2) ": string | null;" - returnType.Replace(":", "").Replace(";", "").Trim(); - - internal (List Parameters, JavaScriptMethod? JavaScriptMethod) ParseParameters( - string typeName, - string rawName, - string parametersString, - Action appendDependentType) + private CSharpAction ToAction(InterfaceDeclaration typescriptInterface) { - List parameters = new(); + var csharpAction = new CSharpAction(typescriptInterface.Identifier); - // Example input: - // "(someCallback: CallbackType, someId?: number | null)" - var trimmedParameters = parametersString.Replace("(", "").Replace(")", ""); - var parameterLineTokenizer = trimmedParameters.Split(new[] { ':', ',', }); + var callSignatureDeclaration = typescriptInterface.OfKind(TypeScriptSyntaxKind.CallSignature).FirstOrDefault() as CallSignatureDeclaration; - JavaScriptMethod? javaScriptMethod = new(rawName); - foreach (var parameterPair in parameterLineTokenizer.Where(t => t.Length > 0).Chunk(2)) + if (callSignatureDeclaration is not null) { - var isNullable = parameterPair[0].EndsWith("?"); - var parameterName = parameterPair[0].Replace("?", "").Trim(); - var parameterType = isNullable - ? parameterPair[1].Trim().Replace(" | null", "") - : parameterPair[1].Trim(); - - CSharpAction? action = null; + var actionParameters = callSignatureDeclaration.Parameters; + var actionReturnType = GetNodeText(callSignatureDeclaration.Type); - // When a parameter defines a custom type, that type needs to also be parsed - // and source generated. This is so that dependent types are known and resolved. - if (!TypeMap.PrimitiveTypes.IsPrimitiveType(parameterType) && - _reader.TryGetDeclaration(parameterType, out var typeScriptDefinitionText) && - typeScriptDefinitionText is not null) + if (actionParameters is null || string.IsNullOrEmpty(actionReturnType)) { - javaScriptMethod = javaScriptMethod with - { - InvokableMethodName = $"blazorators.{typeName.LowerCaseFirstLetter()}.{rawName}" - }; - - if (parameterType.EndsWith("Callback")) - { - action = ToAction(typeScriptDefinitionText); - javaScriptMethod = javaScriptMethod with - { - IsBiDirectionalJavaScript = true - }; - } - else - { - var obj = ToObject(typeScriptDefinitionText); - if (obj is not null) - { - appendDependentType(obj); - } - } + return csharpAction; } - parameters.Add(new(parameterName, parameterType, isNullable, action)); - } + var (csharpParameters, _) = ParseParameters( + csharpAction.RawName, + csharpAction.RawName, + actionParameters, + dependency => csharpAction.DependentTypes[dependency.TypeName] = dependency); - javaScriptMethod = javaScriptMethod with - { - ParameterDefinitions = parameters - }; - - return (parameters, javaScriptMethod); - } - - internal static bool IsAction( - string line, out Match? match) - { - match = TypeScriptCallbackRegex.Match(line); - return match.Success; - } - - internal static bool IsMethod( - string line, out Match? match) - { - match = TypeScriptMethodRegex.Match(line); - var isSuccess = match.Success; - if (isSuccess) - { - var methodName = match.GetGroupValue("MethodName"); - if (methodName is "addEventListener" or "removeEventListener") + csharpAction = csharpAction with { - return false; - } - - var returnType = match.GetGroupValue("ReturnType"); - if (returnType?.Contains("this") ?? false) - { - return false; - } + ParameterDefinitions = csharpParameters + }; } - return isSuccess; + return csharpAction; } - internal static bool IsProperty( - string line, - out Match? match) + private CSharpMethod ToMethod(string methodName, string methodReturnType, IList csharpParameters, JavaScriptMethod javascriptMethod) { - match = TypeScriptPropertyRegex.Match(line); - var isSuccess = match.Success; - if (isSuccess) - { - var name = match.GetGroupValue("Name"); - if (name is "addEventListener" or "removeEventListener" || - (name is not null && name.Contains("this"))) - { - return false; - } + var csharpMethod = new CSharpMethod(methodName, methodReturnType, csharpParameters, javascriptMethod); + var nonGenericMethodReturnType = methodReturnType.ExtractGenericType(); + var nonArrayMethodReturnType = nonGenericMethodReturnType.Replace("[]", ""); - if (match.GetGroupValue("Type") is "void") + if (TryGetCustomType(nonArrayMethodReturnType, out var typescriptInterface)) + { + var csharpObject = ToObject(typescriptInterface); + if (csharpObject is not null) { - return false; + csharpMethod.DependentTypes[nonArrayMethodReturnType] = csharpObject; } } - return isSuccess; + return csharpMethod; } -} -static class EnumerableExtensions -{ - internal static IEnumerable Chunk(this IEnumerable source, int chunksize) + private bool TryGetCustomType(string typeName, out InterfaceDeclaration typescriptInterface) { - while (source.Any()) - { - yield return source.Take(chunksize).ToArray(); - source = source.Skip(chunksize); - } + typescriptInterface = default!; + return !Primitives.IsPrimitiveType(typeName) && + _reader.TryGetInterface(typeName, out typescriptInterface!) && + typescriptInterface is not null; } -} +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs index 2921982f..60267859 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs @@ -6,8 +6,7 @@ namespace Blazor.SourceGenerators.Parsers; internal sealed partial class TypeDeclarationParser { static readonly Lazy s_defaultParser = - new( - valueFactory: () => new TypeDeclarationParser(TypeDeclarationReader.Default)); + new(valueFactory: () => new TypeDeclarationParser(TypeDeclarationReader.Default)); readonly TypeDeclarationReader _reader; @@ -19,15 +18,14 @@ public ParserResult ParseTargetType(string typeName) { ParserResult result = new(ParserResultStatus.Unknown); - if (_reader.TryGetDeclaration(typeName, out var typeScriptDefinitionText) && - typeScriptDefinitionText is not null) + if (_reader.TryGetInterface(typeName, out var typescriptInterface) && typescriptInterface is not null) { try { result = result with { Status = ParserResultStatus.SuccessfullyParsed, - Value = ToTopLevelObject(typeScriptDefinitionText) + Value = ToTopLevelObject(typescriptInterface) }; } catch (Exception ex) diff --git a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.EmbeddedResource.cs b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.EmbeddedResource.cs index 67f6782b..d4e29e49 100644 --- a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.EmbeddedResource.cs +++ b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.EmbeddedResource.cs @@ -5,8 +5,7 @@ namespace Blazor.SourceGenerators.Readers; internal sealed partial class TypeDeclarationReader { - string GetEmbeddedResourceText( - string resourceName = "Blazor.SourceGenerators.Data.lib.dom.d.ts") + string GetEmbeddedResourceText(string resourceName = "Blazor.SourceGenerators.Data.lib.dom.d.ts") { using var stream = typeof(TypeDeclarationReader).Assembly .GetManifestResourceStream(resourceName)!; @@ -16,3 +15,15 @@ string GetEmbeddedResourceText( return reader.ReadToEnd(); } } + +internal sealed partial class TypeDeclarationReader +{ + private readonly HttpClient _client = new(); + + string GetRemoteResourceText(string resourceUri) + { + return _client.GetStringAsync(resourceUri) + .GetAwaiter() + .GetResult(); + } +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.Factory.cs b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.Factory.cs index 2279ec32..e4bffb47 100644 --- a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.Factory.cs +++ b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.Factory.cs @@ -1,8 +1,6 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. -using System; - namespace Blazor.SourceGenerators.Readers; internal sealed partial class TypeDeclarationReader @@ -17,16 +15,14 @@ internal static TypeDeclarationReader FactoryEmbedded() internal static TypeDeclarationReader Factory(string source) { - return Default; - - //var uri = new Uri(source); - //var sourceKey = uri.IsFile ? uri.LocalPath : uri.OriginalString; + var uri = new Uri(source); + var sourceKey = uri.IsFile ? uri.LocalPath : uri.OriginalString; - //var reader = - // s_readerCache.GetOrAdd( - // sourceKey, _ => new TypeDeclarationReader(uri)); + var reader = + s_readerCache.GetOrAdd( + sourceKey, _ => new TypeDeclarationReader(uri)); - //return reader; + return reader; } internal static TypeDeclarationReader Default diff --git a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.LocalFiles.cs b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.LocalFiles.cs deleted file mode 100644 index 64456419..00000000 --- a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.LocalFiles.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) David Pine. All rights reserved. -// Licensed under the MIT License. - -namespace Blazor.SourceGenerators.Readers; - -internal sealed partial class TypeDeclarationReader -{ - string GetLocalFileText(string filePath) => File.ReadAllText(filePath); -} diff --git a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.RemoteFile.cs b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.RemoteFile.cs deleted file mode 100644 index 9469c2a3..00000000 --- a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.RemoteFile.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) David Pine. All rights reserved. -// Licensed under the MIT License. - -namespace Blazor.SourceGenerators.Readers; - -internal sealed partial class TypeDeclarationReader -{ - //static readonly HttpClient s_httpClient = new(); - //static readonly Uri s_defaultTypeDeclarationSource = - // new("https://raw.githubusercontent.com/microsoft/TypeScript/57394450db7e8928b5293eda5b69e853d1c7161e/lib/lib.dom.d.ts"); - // - //string GetRemoteFileText(string url) - //{ - // var typeDeclarationText = - // s_httpClient.GetStringAsync(url) - // .ConfigureAwait(false) - // .GetAwaiter() - // .GetResult(); - // - // return typeDeclarationText; - //} -} diff --git a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs index 71c22232..84949010 100644 --- a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs +++ b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs @@ -1,102 +1,52 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +using Blazor.SourceGenerators.TypeScript; +using Blazor.SourceGenerators.TypeScript.Types; + namespace Blazor.SourceGenerators.Readers; internal sealed partial class TypeDeclarationReader { - readonly Uri? _typeDeclarationSource; - readonly Lazy _typeDeclarationText; + private readonly Lazy _typeDeclarationText; + private ITypeScriptAbstractSyntaxTree? _typescriptAbstractSyntaxTree; - IDictionary? _typeDeclarationMap; - IDictionary? _typeAliasMap; + private TypeDeclarationReader(Uri typeDeclarationUri) + { + _typeDeclarationText = new Lazy(valueFactory: () => typeDeclarationUri switch + { + { IsAbsoluteUri: true } => GetRemoteResourceText(typeDeclarationUri.AbsoluteUri), + _ => GetEmbeddedResourceText() + }); + } - private IDictionary TypeDeclarationMap => - _typeDeclarationMap ??= ReadTypeDeclarationMap(_typeDeclarationText.Value); + private TypeDeclarationReader() : this(null!) + { + } - private IDictionary TypeAliasMap => - _typeAliasMap ??= ReadTypeAliasMap(_typeDeclarationText.Value); + /// + /// For testing purposes. + /// + internal bool IsInitialized => TypescriptAbstractSyntaxTree.RootNode is { Count: > 0 }; internal string RawSourceText => _typeDeclarationText.Value; - private TypeDeclarationReader( - Uri? typeDeclarationSource = null) - { - _typeDeclarationSource = typeDeclarationSource; - _typeDeclarationText = new Lazy( - valueFactory: () => GetEmbeddedResourceText()); - } + private ITypeScriptAbstractSyntaxTree TypescriptAbstractSyntaxTree => + _typescriptAbstractSyntaxTree ??= TypeScriptAbstractSyntaxTree.FromSourceText(_typeDeclarationText.Value); - ConcurrentDictionary ReadTypeDeclarationMap(string typeDeclarations) + public bool TryGetInterface(string typeName, out InterfaceDeclaration? declaration) { - ConcurrentDictionary map = new(); + declaration = TypescriptAbstractSyntaxTree.RootNode.OfKind(TypeScriptSyntaxKind.InterfaceDeclaration) + .FirstOrDefault(node => node.Identifier == typeName) as InterfaceDeclaration; - try - { - if (typeDeclarations is { Length: > 0 }) - { - var matchCollection = - InterfaceRegex.Matches(typeDeclarations).Cast().Select(m => m.Value); - Parallel.ForEach( - matchCollection, - match => - { - var typeName = InterfaceTypeNameRegex.GetMatchGroupValue(match, "TypeName"); - if (typeName is not null) - { - map[typeName] = match; - } - }); - } - } - catch (Exception ex) - { - Console.WriteLine($"Error initializing lib dom parser. {ex}"); - } - - return map; + return declaration != null; } - ConcurrentDictionary ReadTypeAliasMap(string typeDeclarations) + public bool TryGetTypeAlias(string typeName, out TypeAliasDeclaration? declaration) { - ConcurrentDictionary map = new(); - - try - { - if (typeDeclarations is { Length: > 0 }) - { - var matchCollection = - TypeRegex.Matches(typeDeclarations).Cast().Select(m => m.Value); - Parallel.ForEach( - matchCollection, - match => - { - var typeName = TypeNameRegex.GetMatchGroupValue(match, "TypeName"); - if (typeName is not null) - { - map[typeName] = match; - } - }); - } - } - catch (Exception ex) - { - Console.WriteLine($"Error initializing lib dom parser. {ex}"); - } + declaration = TypescriptAbstractSyntaxTree.RootNode.OfKind(TypeScriptSyntaxKind.TypeAliasDeclaration) + .FirstOrDefault(node => node.Identifier == typeName) as TypeAliasDeclaration; - return map; + return declaration != null; } - - /// - /// For testing purposes. - /// - internal bool IsInitialized => TypeDeclarationMap is { Count: > 0 }; - - public bool TryGetDeclaration( - string typeName, out string? declaration) => - TypeDeclarationMap.TryGetValue(typeName, out declaration); - - public bool TryGetTypeAlias( - string typeAliasName, out string? typeAlias) => - TypeAliasMap.TryGetValue(typeAliasName, out typeAlias); -} +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/TypeScript/Compiler/Core.cs b/src/Blazor.SourceGenerators/TypeScript/Compiler/Core.cs index 520e3f21..ecc206e3 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Compiler/Core.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Compiler/Core.cs @@ -20,7 +20,11 @@ internal static int BinarySearch( } var low = offset ?? 0; var high = array.Length - 1; - comparer ??= (v1, v2) => v1 < v2 ? -1 : v1 > v2 ? 1 : 0; + comparer ??= (v1, v2) => + { + if (v1 < v2) return -1; + return v1 > v2 ? 1 : 0; + }; while (low <= high) { var middle = low + ((high - low) >> 1); @@ -41,10 +45,16 @@ internal static int BinarySearch( return ~low; } +#pragma warning disable S125 // Sections of code should not be commented out +#pragma warning disable S1940 // Boolean checks should not be inverted + internal static bool PositionIsSynthesized(int pos) => - // This is a fast way of testing the following conditions: - // pos is null || pos is null || isNaN(pos) || pos < 0; - !(pos >= 0); + // This is a fast way of testing the following conditions: + // pos is null || pos is null || isNaN(pos) || pos < 0; + !(pos >= 0); + +#pragma warning restore S1940 // Boolean checks should not be inverted +#pragma warning restore S125 // Sections of code should not be commented out internal static ScriptKind EnsureScriptKind(string fileName, ScriptKind scriptKind) { @@ -74,7 +84,7 @@ internal static string NormalizePath(string path) var rootLength = GetRootLength(path); var root = path.Substring(rootLength); var normalized = GetNormalizedParts(path, rootLength); - if (normalized.Any()) + if (normalized.Count != 0) { var joinedParts = $"{root}{string.Join(DirectorySeparator.ToString(), normalized)}"; return PathEndsWithDirectorySeparator(path) ? joinedParts + DirectorySeparator : joinedParts; @@ -119,7 +129,7 @@ internal static int GetRootLength(string path) internal static List GetNormalizedParts(string normalizedSlashedPath, int rootLength) { var parts = normalizedSlashedPath.Substring(rootLength).Split(DirectorySeparator); - List normalized = new(); + List normalized = []; foreach (var part in parts) { if (part != ".") @@ -143,8 +153,8 @@ internal static List GetNormalizedParts(string normalizedSlashedPath, in } internal static T LastOrUndefined(List array) where T : class => - array != null && array.Any() - ? array.Last() + array != null && array.Count != 0 + ? array[array.Count - 1] : default; internal static bool PathEndsWithDirectorySeparator(string path) => diff --git a/src/Blazor.SourceGenerators/TypeScript/Compiler/Diagnostics.cs b/src/Blazor.SourceGenerators/TypeScript/Compiler/Diagnostics.cs index d03c03a9..b6bfadea 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Compiler/Diagnostics.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Compiler/Diagnostics.cs @@ -5,103 +5,103 @@ namespace Blazor.SourceGenerators.TypeScript.Compiler; -internal class Diagnostics +internal static class Diagnostics { - public static DiagnosticMessage Merge_conflict_marker_encountered = + public static readonly DiagnosticMessage Merge_conflict_marker_encountered = DiagnosticMessage.Error("Merge conflict marker encountered.", 1185); - public static DiagnosticMessage Digit_expected = + public static readonly DiagnosticMessage Digit_expected = DiagnosticMessage.Error("Digit expected.", 1124); - public static DiagnosticMessage Unterminated_string_literal = + public static readonly DiagnosticMessage Unterminated_string_literal = DiagnosticMessage.Error("Unterminated string literal.", 1002); - public static DiagnosticMessage Unterminated_template_literal = + public static readonly DiagnosticMessage Unterminated_template_literal = DiagnosticMessage.Error("Unterminated template literal.", 1160); - public static DiagnosticMessage Unexpected_end_of_text = + public static readonly DiagnosticMessage Unexpected_end_of_text = DiagnosticMessage.Error("Unexpected end of text.", 1126); - public static DiagnosticMessage Hexadecimal_digit_expected = + public static readonly DiagnosticMessage Hexadecimal_digit_expected = DiagnosticMessage.Error("Hexadecimal digit expected.", 1125); - public static DiagnosticMessage An_extended_Unicode_escape_value_must_be_between_0x0_and_0x10FFFF_inclusive = + public static readonly DiagnosticMessage An_extended_Unicode_escape_value_must_be_between_0x0_and_0x10FFFF_inclusive = DiagnosticMessage.Error("An extended Unicode escape value must be between 0x0 and 0x10FFFF inclusive.", 1198); - public static DiagnosticMessage Unterminated_Unicode_escape_sequence = + public static readonly DiagnosticMessage Unterminated_Unicode_escape_sequence = DiagnosticMessage.Error("Unterminated Unicode escape sequence.", 1199); - public static DiagnosticMessage Asterisk_Slash_expected; + public static readonly DiagnosticMessage Asterisk_Slash_expected; - public static DiagnosticMessage Binary_digit_expected; + public static readonly DiagnosticMessage Binary_digit_expected; - public static DiagnosticMessage Octal_digit_expected; + public static readonly DiagnosticMessage Octal_digit_expected; - public static DiagnosticMessage Invalid_character; + public static readonly DiagnosticMessage Invalid_character; - public static DiagnosticMessage Unterminated_regular_expression_literal; + public static readonly DiagnosticMessage Unterminated_regular_expression_literal; - public static DiagnosticMessage _0_expected; + public static readonly DiagnosticMessage _0_expected; - public static DiagnosticMessage Identifier_expected; + public static readonly DiagnosticMessage Identifier_expected; - public static DiagnosticMessage Declaration_or_statement_expected; + public static readonly DiagnosticMessage Declaration_or_statement_expected; - public static DiagnosticMessage case_or_default_expected; + public static readonly DiagnosticMessage case_or_default_expected; - public static DiagnosticMessage Statement_expected; + public static readonly DiagnosticMessage Statement_expected; - public static DiagnosticMessage Property_or_signature_expected; + public static readonly DiagnosticMessage Property_or_signature_expected; - public static DiagnosticMessage Unexpected_token_A_constructor_method_accessor_or_property_was_expected; + public static readonly DiagnosticMessage Unexpected_token_A_constructor_method_accessor_or_property_was_expected; - public static DiagnosticMessage Enum_member_expected; + public static readonly DiagnosticMessage Enum_member_expected; - public static DiagnosticMessage Expression_expected; + public static readonly DiagnosticMessage Expression_expected; - public static DiagnosticMessage Variable_declaration_expected; + public static readonly DiagnosticMessage Variable_declaration_expected; - public static DiagnosticMessage Property_destructuring_pattern_expected; + public static readonly DiagnosticMessage Property_destructuring_pattern_expected; - public static DiagnosticMessage Array_element_destructuring_pattern_expected; + public static readonly DiagnosticMessage Array_element_destructuring_pattern_expected; - public static DiagnosticMessage Argument_expression_expected; + public static readonly DiagnosticMessage Argument_expression_expected; - public static DiagnosticMessage Property_assignment_expected; + public static readonly DiagnosticMessage Property_assignment_expected; - public static DiagnosticMessage Expression_or_comma_expected; + public static readonly DiagnosticMessage Expression_or_comma_expected; - public static DiagnosticMessage Parameter_declaration_expected; + public static readonly DiagnosticMessage Parameter_declaration_expected; - public static DiagnosticMessage Type_parameter_declaration_expected; + public static readonly DiagnosticMessage Type_parameter_declaration_expected; - public static DiagnosticMessage Type_argument_expected; + public static readonly DiagnosticMessage Type_argument_expected; - public static DiagnosticMessage Type_expected; + public static readonly DiagnosticMessage Type_expected; - public static DiagnosticMessage Unexpected_token_expected; + public static readonly DiagnosticMessage Unexpected_token_expected; - public static DiagnosticMessage A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses; + public static readonly DiagnosticMessage A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses; - public static DiagnosticMessage An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses; + public static readonly DiagnosticMessage An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses; - public static DiagnosticMessage super_must_be_followed_by_an_argument_list_or_member_access; + public static readonly DiagnosticMessage super_must_be_followed_by_an_argument_list_or_member_access; - public static DiagnosticMessage Expected_corresponding_JSX_closing_tag_for_0; + public static readonly DiagnosticMessage Expected_corresponding_JSX_closing_tag_for_0; - public static DiagnosticMessage JSX_expressions_must_have_one_parent_element; + public static readonly DiagnosticMessage JSX_expressions_must_have_one_parent_element; - public static DiagnosticMessage JSX_element_0_has_no_corresponding_closing_tag; + public static readonly DiagnosticMessage JSX_element_0_has_no_corresponding_closing_tag; - public static DiagnosticMessage Declaration_expected; + public static readonly DiagnosticMessage Declaration_expected; - public static DiagnosticMessage or_expected; + public static readonly DiagnosticMessage or_expected; - public static DiagnosticMessage An_AMD_module_cannot_have_multiple_name_assignments; + public static readonly DiagnosticMessage An_AMD_module_cannot_have_multiple_name_assignments; - public static DiagnosticMessage Type_argument_list_cannot_be_empty; + public static readonly DiagnosticMessage Type_argument_list_cannot_be_empty; - public static DiagnosticMessage Trailing_comma_not_allowed; + public static readonly DiagnosticMessage Trailing_comma_not_allowed; - public static DiagnosticMessage _0_tag_already_specified; + public static readonly DiagnosticMessage _0_tag_already_specified; } \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/TypeScript/Compiler/Factory.cs b/src/Blazor.SourceGenerators/TypeScript/Compiler/Factory.cs index 0321e74d..32273c41 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Compiler/Factory.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Compiler/Factory.cs @@ -5,9 +5,9 @@ namespace Blazor.SourceGenerators.TypeScript.Compiler; -internal static class Factory +internal static class Factory { - internal static INode SkipPartiallyEmittedExpressions(INode node) + internal static INode SkipPartiallyEmittedExpressions(INode node) { while (node is { Kind: TypeScriptSyntaxKind.PartiallyEmittedExpression } and PartiallyEmittedExpression expression) diff --git a/src/Blazor.SourceGenerators/TypeScript/Compiler/JsDocParser.cs b/src/Blazor.SourceGenerators/TypeScript/Compiler/JsDocParser.cs index 15d488e4..889acb99 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Compiler/JsDocParser.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Compiler/JsDocParser.cs @@ -1,7 +1,6 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. -using System.Diagnostics; using Blazor.SourceGenerators.TypeScript.Types; using static Blazor.SourceGenerators.TypeScript.Compiler.Scanner; using SyntaxKind = Blazor.SourceGenerators.TypeScript.Types.TypeScriptSyntaxKind; @@ -28,16 +27,16 @@ internal class JsDocParser private SyntaxKind Token => Parser.CurrentToken; private SyntaxKind NextToken => Parser.NextToken(); private T TryParse(Func callback) => Parser.TryParse(callback); - private bool ParseExpected(SyntaxKind kind, DiagnosticMessage? diagnosticMessage = null, bool shouldAdvance = true) => Parser.ParseExpected(kind, diagnosticMessage, shouldAdvance); + private void ParseExpected(SyntaxKind kind, DiagnosticMessage? diagnosticMessage = null, bool shouldAdvance = true) => Parser.ParseExpected(kind, diagnosticMessage, shouldAdvance); private bool ParseOptional(SyntaxKind t) => Parser.ParseOptional(t); - private INode ParseOptionalToken(SyntaxKind t) where T : Node, new() => Parser.ParseOptionalToken(t); + private Node ParseOptionalToken(SyntaxKind t) where T : Node, new() => Parser.ParseOptionalToken(t); private T ParseTokenNode() where T : Node, new() => Parser.ParseTokenNode(Token); private NodeArray CreateList(T[] elements = null, int? pos = null) => Parser.CreateList(elements, pos); private T FinishNode(T node, int? end = null) where T : Node => Parser.FinishNode(node, end); private Identifier ParseIdentifierName() => Parser.ParseIdentifierName(); private NodeArray ParseDelimitedList(ParsingContext kind, Func parseElement, bool? considerSemicolonAsDelimiter = null) where T : INode => Parser.ParseDelimitedList(kind, parseElement, considerSemicolonAsDelimiter); private TypeLiteralNode ParseTypeLiteral() => Parser.ParseTypeLiteral(); - private IExpression ParseExpression() => Parser.ParseExpression(); + private void ParseExpression() => Parser.ParseExpression(); public bool IsJsDocType() => Token switch { @@ -51,11 +50,13 @@ public static (JsDocTypeExpression res, List diagnostics) var languageVersion = ScriptTarget.Latest; dp.Parser.InitializeState(content, languageVersion, ScriptKind.Js); - var sourceFile = dp.Parser.CreateSourceFile("file.js", languageVersion, ScriptKind.Js); + /* sourceFile */ + dp.Parser.CreateSourceFile("file.js", languageVersion, ScriptKind.Js); dp.Parser.Scanner.SetText(content, start, length); - var currentToken = dp.Parser.Scanner.Scan(); + /* currentToken */ + dp.Parser.Scanner.Scan(); var jsDocTypeExpression = dp.ParseJsDocTypeExpression(); var diagnostics = dp.Parser.ParseDiagnostics; @@ -115,8 +116,7 @@ public IJsDocType ParseJsDocType() type = FinishNode(arrayType); } - else - if (Token == SyntaxKind.QuestionToken) + else if (Token == SyntaxKind.QuestionToken) { var nullableType = new JsDocNullableType { Type = type }; @@ -124,8 +124,7 @@ public IJsDocType ParseJsDocType() type = FinishNode(nullableType); } - else - if (Token == SyntaxKind.ExclamationToken) + else if (Token == SyntaxKind.ExclamationToken) { var nonNullableType = new JsDocNonNullableType { Type = type }; @@ -274,7 +273,7 @@ public NodeArray ParseTypeArguments() public void CheckForEmptyTypeArgumentList(NodeArray typeArguments) { - if (!ParseDiagnostics.Any() && typeArguments != null && !typeArguments.Any()) + if (ParseDiagnostics.Count == 0 && typeArguments != null && !typeArguments.Any()) { var start = (typeArguments.Pos ?? 0) - "<".Length; var end = SkipTriviaM(SourceText, (int)typeArguments.End) + ">".Length; @@ -443,8 +442,8 @@ public JsDoc ParseJsDocCommentWorker(int? start = null, int? length = null) Debug.Assert(strt <= end); Debug.Assert(end <= content.Length); - NodeArray tags = new(); - List comments = new(); + NodeArray tags = []; + List comments = []; JsDoc result = null; if (!IsJsDocStart(content, strt)) { @@ -522,18 +521,21 @@ public JsDoc ParseJsDocCommentWorker(int? start = null, int? length = null) break; case SyntaxKind.WhitespaceTrivia: var whitespace = Scanner.TokenText; +#pragma warning disable S2583 // Conditionally executed code should be reachable + if (state == JSDocState.SavingComments) { comments.Add(whitespace); } - else - if (margin != null && (indent ?? 0) + whitespace.Length > margin) + else if (margin != null && (indent ?? 0) + whitespace.Length > margin) { comments.Add(whitespace.Slice((int)margin - (indent ?? 0) - 1)); } +#pragma warning restore S2583 // Conditionally executed code should be reachable + indent += whitespace.Length; break; @@ -577,7 +579,7 @@ void PushComment(string text) void RemoveLeadingNewlines(List comments3) { - while (comments3.Any() && (comments3[0] == "\n" || comments3[0] == "\r")) + while (comments3.Count != 0 && (comments3[0] == "\n" || comments3[0] == "\r")) { comments3 = comments3.Skip(1).ToList(); } @@ -585,7 +587,7 @@ void RemoveLeadingNewlines(List comments3) void RemoveTrailingNewlines(List comments2) { - while (comments2.Any() && (comments2[comments2.Count - 1] == "\n" || comments2[comments2.Count - 1] == "\r")) + while (comments2.Count != 0 && (comments2[comments2.Count - 1] == "\n" || comments2[comments2.Count - 1] == "\r")) { comments2.Pop(); } @@ -601,7 +603,7 @@ JsDoc CreateJsDocComment() var result2 = new JsDoc { Tags = tags, - Comment = comments.Any() ? string.Join("", comments) : null + Comment = comments.Count != 0 ? string.Join("", comments) : null }; return FinishNode(result2, end); @@ -631,6 +633,8 @@ void ParseTag(int indent) return; } IJsDocTag tag = null; +#pragma warning disable S2583 // Conditionally executed code should be reachable + if (tagName != null) { tag = tagName.Text switch @@ -649,6 +653,8 @@ void ParseTag(int indent) tag = ParseUnknownTag(atToken, tagName); } + +#pragma warning restore S2583 // Conditionally executed code should be reachable if (tag == null) { @@ -660,7 +666,7 @@ void ParseTag(int indent) List ParseTagComments(int indent) { - List comments2 = new(); + List comments2 = []; var state = JSDocState.SawAsterisk; int? margin = null; while (Token != SyntaxKind.AtToken && Token != SyntaxKind.EndOfFileToken) @@ -691,12 +697,16 @@ List ParseTagComments(int indent) else { var whitespace = Scanner.TokenText; +#pragma warning disable S2583 // Conditionally executed code should be reachable + if (margin != null && indent + whitespace.Length > margin) { comments2.Add(whitespace.Slice((int)margin - indent - 1)); } +#pragma warning restore S2583 // Conditionally executed code should be reachable + indent += whitespace.Length; } @@ -711,7 +721,9 @@ List ParseTagComments(int indent) break; } +#pragma warning disable S907 // "goto" statement should not be used goto caseLabel5; +#pragma warning restore S907 // "goto" statement should not be used default: caseLabel5: state = JSDocState.SavingComments; @@ -802,8 +814,7 @@ JsDocParameterTag ParseParamTag(AtToken atToken, Identifier tagName) ParseExpected(SyntaxKind.CloseBracketToken); } - else - if (TokenIsIdentifierOrKeyword(Token)) + else if (TokenIsIdentifierOrKeyword(Token)) { name = ParseJsDocIdentifierName(); @@ -843,7 +854,7 @@ JsDocParameterTag ParseParamTag(AtToken atToken, Identifier tagName) JsDocReturnTag ParseReturnTag(AtToken atToken, Identifier tagName) { - if (tags.Any(t => t.Kind == SyntaxKind.JsDocReturnTag)) + if (tags.Exists(t => t.Kind == SyntaxKind.JsDocReturnTag)) { ParseErrorAtPosition(tagName.Pos ?? 0, Scanner.TokenPos - (tagName.Pos ?? 0), Diagnostics._0_tag_already_specified, tagName.Text); @@ -860,7 +871,7 @@ JsDocReturnTag ParseReturnTag(AtToken atToken, Identifier tagName) JsDocTypeTag ParseTypeTag(AtToken atToken, Identifier tagName) { - if (tags.Any(t => t.Kind == SyntaxKind.JsDocTypeTag)) + if (tags.Exists(t => t.Kind == SyntaxKind.JsDocTypeTag)) { ParseErrorAtPosition(tagName.Pos ?? 0, Scanner.TokenPos - (tagName.Pos ?? 0), Diagnostics._0_tag_already_specified, tagName.Text); @@ -931,10 +942,10 @@ IJsDocTag ParseTypedefTag(AtToken atToken, Identifier tagName) var rightNode = typedefTag.FullName; while (true) { - if (rightNode.Kind == SyntaxKind.Identifier || (rightNode as JsDocNamespaceDeclaration)?.Body == null) + if (rightNode?.Kind == SyntaxKind.Identifier || (rightNode as JsDocNamespaceDeclaration)?.Body == null) { - typedefTag.Name = rightNode.Kind == SyntaxKind.Identifier ? rightNode : (rightNode as JsDocTypedefTag)?.Name; + typedefTag.Name = rightNode?.Kind == SyntaxKind.Identifier ? rightNode : (rightNode as JsDocTypedefTag)?.Name; break; } @@ -1021,10 +1032,11 @@ JsDocTypeLiteral ScanChildTags() case SyntaxKind.Identifier: canParseTag = false; +#pragma warning disable S907 // "goto" statement should not be used goto caseLabel5; +#pragma warning restore S907 // "goto" statement should not be used case SyntaxKind.EndOfFileToken: -caseLabel5: - break; +caseLabel5: break; } } @@ -1035,7 +1047,6 @@ JsDocTypeLiteral ScanChildTags() INode ParseJsDocTypeNameWithNamespace(NodeFlags flags) { - var pos = Scanner.TokenPos; var typeNameOrNamespaceName = ParseJsDocIdentifierName(); if (typeNameOrNamespaceName != null && ParseOptional(SyntaxKind.DotToken)) { @@ -1088,7 +1099,7 @@ bool TryParseChildTag(JsDocTypeLiteral parentTag) var propertyTag = ParsePropertyTag(atToken, tagName); if (propertyTag != null) { - parentTag.JsDocPropertyTags ??= new NodeArray(); + parentTag.JsDocPropertyTags ??= []; parentTag.JsDocPropertyTags.Add(propertyTag); @@ -1103,7 +1114,7 @@ bool TryParseChildTag(JsDocTypeLiteral parentTag) JsDocTemplateTag ParseTemplateTag(AtToken atToken, Identifier tagName) { - if (tags.Any(t => t.Kind == SyntaxKind.JsDocTemplateTag)) + if (tags.Exists(t => t.Kind == SyntaxKind.JsDocTemplateTag)) { ParseErrorAtPosition(tagName.Pos ?? 0, Scanner.TokenPos - (tagName.Pos ?? 0), Diagnostics._0_tag_already_specified, tagName.Text); @@ -1151,10 +1162,9 @@ JsDocTemplateTag ParseTemplateTag(AtToken atToken, Identifier tagName) return result3; } - SyntaxKind NextJsDocToken() + void NextJsDocToken() { CurrentToken = Scanner.ScanJsDocToken(); - return CurrentToken; } Identifier ParseJsDocIdentifierName() => CreateJsDocIdentifier(TokenIsIdentifierOrKeyword(Token)); diff --git a/src/Blazor.SourceGenerators/TypeScript/Compiler/Parser.cs b/src/Blazor.SourceGenerators/TypeScript/Compiler/Parser.cs index 95346e55..5f98eeac 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Compiler/Parser.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Compiler/Parser.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. #nullable disable -using System.Diagnostics; using Blazor.SourceGenerators.TypeScript.Types; using static Blazor.SourceGenerators.TypeScript.Compiler.Core; using static Blazor.SourceGenerators.TypeScript.Compiler.Scanner; @@ -13,8 +12,9 @@ namespace Blazor.SourceGenerators.TypeScript.Compiler; -[System.Diagnostics.CodeAnalysis.SuppressMessage( - "Style", "IDE0007:Use implicit type", Justification = "Leave explicitly typed for future NRT work.")] +#pragma warning disable S125 // Sections of code should not be commented out + +[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0007:Use implicit type", Justification = "Leave explicitly typed for future NRT work.")] internal sealed class Parser { @@ -67,12 +67,12 @@ public IEntityName ParseIsolatedEntityName(string content, ScriptTarget language // Prime the scanner. NextToken(); var entityName = ParseEntityName(true); - var isInvalid = CurrentToken == SyntaxKind.EndOfFileToken && !ParseDiagnostics.Any(); + var isInvalid = CurrentToken == SyntaxKind.EndOfFileToken && ParseDiagnostics.Count == 0; ClearState(); return isInvalid ? entityName : null; } - public LanguageVariant GetLanguageVariant(ScriptKind scriptKind) => + public static LanguageVariant GetLanguageVariant(ScriptKind scriptKind) => // .tsx and .jsx files are treated as jsx language variant. scriptKind is ScriptKind.Tsx or ScriptKind.Jsx or ScriptKind.Js ? LanguageVariant.Jsx @@ -81,9 +81,9 @@ scriptKind is ScriptKind.Tsx or ScriptKind.Jsx or ScriptKind.Js public void InitializeState(string sourceText, ScriptTarget languageVersion, ScriptKind scriptKind) { SourceText = sourceText; - ParseDiagnostics = new(); + ParseDiagnostics = []; ParsingContext = 0; - Identifiers = new(); + Identifiers = []; IdentifierCount = 0; NodeCount = 0; ContextFlags = scriptKind is ScriptKind.Js or ScriptKind.Jsx ? NodeFlags.JavaScriptFile : NodeFlags.None; @@ -132,7 +132,7 @@ public RootNodeSourceFile ParseSourceFileWorker(string fileName, ScriptTarget la public T AddJsDocComment(T node) where T : INode { var comments = GetJsDocCommentRanges(node, SourceFile.Text); - if (comments.Any()) + if (comments.Count != 0) { foreach (var comment in comments) { @@ -141,7 +141,7 @@ public T AddJsDocComment(T node) where T : INode { continue; } - node.JsDoc ??= new List(); + node.JsDoc ??= []; node.JsDoc.Add(jsDoc); } } @@ -152,7 +152,7 @@ public void FixupParentReferences(INode rootNode) { INode parent = rootNode; ForEachChild(rootNode, visitNode); - return; + INode visitNode(INode n) { if (n.Parent != parent) @@ -181,7 +181,7 @@ public RootNodeSourceFile CreateSourceFile(string fileName, ScriptTarget languag var sourceFile = new RootNodeSourceFile { Pos = 0, End = SourceText.Length }; NodeCount++; sourceFile.Text = SourceText; - sourceFile.BindDiagnostics = new(); + sourceFile.BindDiagnostics = []; sourceFile.LanguageVersion = languageVersion; sourceFile.FileName = NormalizePath(fileName); sourceFile.LanguageVariant = GetLanguageVariant(scriptKind); @@ -273,7 +273,7 @@ public void ParseErrorAtCurrentToken(DiagnosticMessage? message, object arg0 = n ParseErrorAtPosition(start, length, message, arg0); } - public void ParseErrorAtPosition(int start, int length, DiagnosticMessage? message, object arg0 = null) + public void ParseErrorAtPosition(int start, int length, DiagnosticMessage? message, object _ = null) { var lastError = LastOrUndefined(ParseDiagnostics); if (lastError == null || start != lastError.Start) @@ -326,7 +326,7 @@ public T SpeculationHelper(Func callback, bool isLookAhead) ? Scanner.LookAhead(callback) : Scanner.TryScan(callback); Debug.Assert(saveContextFlags == ContextFlags); - if (result == null || (result is bool && Convert.ToBoolean(result) is false) || isLookAhead) + if (Equals(result, default(T)) || (result is bool && !Convert.ToBoolean(result)) || isLookAhead) { CurrentToken = saveToken; ParseDiagnostics = ParseDiagnostics.Take(saveParseDiagnosticsLength).ToList(); @@ -428,7 +428,7 @@ public bool ParseSemicolon() public NodeArray CreateList(T[] elements = null, int? pos = null) { - var array = elements == null ? new NodeArray() : new NodeArray(elements); // (List)(elements || []); + var array = elements == null ? [] : new NodeArray(elements); // (List)(elements || []); if (!(pos >= 0)) { pos = GetNodePos(); @@ -453,7 +453,7 @@ public T FinishNode(T node, int? end = null) where T : INode return node; } - public Node CreateMissingNode(SyntaxKind kind, bool reportAtCurrentPosition, DiagnosticMessage? diagnosticMessage = null, object arg0 = null) where T : Node + public Node CreateMissingNode(SyntaxKind _, bool reportAtCurrentPosition, DiagnosticMessage? diagnosticMessage = null, object arg0 = null) where T : Node { if (reportAtCurrentPosition) { @@ -574,7 +574,7 @@ public bool NextTokenCanFollowModifier() public bool ParseAnyContextualModifier() => IsModifierKind(CurrentToken) && TryParse(NextTokenCanFollowModifier); - public bool CanFollowModifier() => CurrentToken is SyntaxKind.OpenBracketToken or SyntaxKind.OpenBraceToken or SyntaxKind.AsteriskToken or SyntaxKind.DotDotDotToken + public bool CanFollowModifier() => CurrentToken is SyntaxKind.OpenBracketToken or SyntaxKind.OpenBraceToken or SyntaxKind.AsteriskToken or SyntaxKind.DotDotDotToken || IsLiteralPropertyName(); public bool NextTokenIsClassOrFunctionOrAsync() @@ -759,12 +759,9 @@ public bool IsInSomeParsingContext() //for (var kind = 0; kind < Enum.GetNames(typeof(ParsingContext)).Count(); kind++) foreach (ParsingContextEnum kind in Enum.GetValues(typeof(ParsingContextEnum))) { - if ((ParsingContext & (1 << (int)kind)) != 0) + if ((ParsingContext & (1 << (int)kind)) != 0 && (IsListElement(kind, true) || IsListTerminator(kind))) { - if (IsListElement(kind, true) || IsListTerminator(kind)) - { - return true; - } + return true; } } return false; @@ -828,9 +825,9 @@ public T ParseListElement2(ParsingContextEnum parsingContext, Func parseEl return node != null ? (T)ConsumeNode(node) : parseElement(); } - public Node CurrentNode(ParsingContextEnum parsingContext) => ParseErrorBeforeNextFinishedNode ? null : null; + public static Node CurrentNode(ParsingContextEnum /* parsingContext */ _) => null!; // ParseErrorBeforeNextFinishedNode ? null : null; - public INode CurrentNode2(ParsingContextEnum parsingContext) => ParseErrorBeforeNextFinishedNode ? null : null; + public static INode CurrentNode2(ParsingContextEnum /* parsingContext */ _) => null!; // ParseErrorBeforeNextFinishedNode ? null : null; public INode ConsumeNode(INode node) { @@ -847,7 +844,7 @@ public INode ConsumeNode(INode node) // return node; //} - public bool CanReuseNode(Node node, ParsingContextEnum parsingContext) + public static bool CanReuseNode(Node node, ParsingContextEnum parsingContext) { switch (parsingContext) { @@ -883,7 +880,7 @@ public bool CanReuseNode(Node node, ParsingContextEnum parsingContext) return false; } - public bool IsReusableClassMember(Node node) + public static bool IsReusableClassMember(Node node) { if (node != null) { @@ -906,7 +903,7 @@ public bool IsReusableClassMember(Node node) return false; } - public bool IsReusableSwitchClause(Node node) + public static bool IsReusableSwitchClause(Node node) { if (node != null) { @@ -920,7 +917,7 @@ public bool IsReusableSwitchClause(Node node) return false; } - public bool IsReusableStatement(Node node) + public static bool IsReusableStatement(Node node) { if (node != null) { @@ -961,9 +958,9 @@ public bool IsReusableStatement(Node node) return false; } - public bool IsReusableEnumMember(Node node) => node.Kind == SyntaxKind.EnumMember; + public static bool IsReusableEnumMember(Node node) => node.Kind == SyntaxKind.EnumMember; - public bool IsReusableTypeMember(Node node) + public static bool IsReusableTypeMember(Node node) { if (node != null) { @@ -980,7 +977,7 @@ public bool IsReusableTypeMember(Node node) return false; } - public bool IsReusableVariableDeclaration(Node node) + public static bool IsReusableVariableDeclaration(Node node) { if (node.Kind != SyntaxKind.VariableDeclaration) { @@ -990,7 +987,7 @@ public bool IsReusableVariableDeclaration(Node node) return variableDeclarator.Initializer == null; } - public bool IsReusableParameter(Node node) + public static bool IsReusableParameter(Node node) { if (node.Kind != SyntaxKind.Parameter) { @@ -1011,7 +1008,7 @@ public bool AbortParsingListOrMoveToNextToken(ParsingContextEnum kind) return false; } - public DiagnosticMessage ParsingContextErrors(ParsingContextEnum context) => context switch + public static DiagnosticMessage ParsingContextErrors(ParsingContextEnum context) => context switch { ParsingContextEnum.SourceElements => Diagnostics.Declaration_or_statement_expected, ParsingContextEnum.BlockStatements => Diagnostics.Declaration_or_statement_expected, @@ -1216,7 +1213,7 @@ public ILiteralLikeNode ParseTemplateMiddleOrTemplateTail() { fragment = ParseLiteralLikeNode(new TemplateTail(), internName: false); } - Debug.Assert(fragment.Kind is SyntaxKind.TemplateMiddle or SyntaxKind.TemplateTail, "Template fragment has wrong token kind"); + Debug.Assert(fragment?.Kind is SyntaxKind.TemplateMiddle or SyntaxKind.TemplateTail, "Template fragment has wrong token kind"); return fragment; } @@ -1323,7 +1320,7 @@ public TypeParameterDeclaration ParseTypeParameter() public NodeArray ParseTypeParameters() => CurrentToken == SyntaxKind.LessThanToken ? ParseBracketedList(ParsingContextEnum.TypeParameters, ParseTypeParameter, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken) - : null; + : null!; public ITypeNode ParseParameterType() => ParseOptional(SyntaxKind.ColonToken) ? ParseType() : null; @@ -1390,8 +1387,7 @@ public void FillSignature(SyntaxKind returnToken, bool ParseExpected(returnToken); signature.Type = ParseTypeOrTypePredicate(); } - else - if (ParseOptional(returnToken)) + else if (ParseOptional(returnToken)) { signature.Type = ParseTypeOrTypePredicate(); } @@ -1411,8 +1407,7 @@ public void FillSignatureEqualsGreaterThanToken(SyntaxKind returnToken, bool ParseExpected(returnToken); signature.Type = ParseTypeOrTypePredicate(); } - else - if (ParseOptional(returnToken)) + else if (ParseOptional(returnToken)) { signature.Type = ParseTypeOrTypePredicate(); } @@ -1433,8 +1428,7 @@ public void FillSignatureColonToken(SyntaxKind ParseExpected(returnToken); signature.Type = ParseTypeOrTypePredicate(); } - else - if (ParseOptional(returnToken)) + else if (ParseOptional(returnToken)) { signature.Type = ParseTypeOrTypePredicate(); } @@ -1455,14 +1449,14 @@ public NodeArray ParseParameterList(bool yieldContext, boo { // Caller insisted that we had to end with a ) We didn't. So just return // null here. - return null; + return null!; } return result; } // We didn't even have an open paren. If the caller requires a complete parameter list, // we definitely can't provide that. However, if they're ok with an incomplete one, // then just return an empty set of parameters. - return requireCompleteParameterList ? null : CreateMissingList(); + return requireCompleteParameterList ? null! : CreateMissingList(); } public void ParseTypeMemberSemicolon() @@ -1531,8 +1525,7 @@ public bool IsUnambiguouslyIndexSignature() return true; } } - else - if (!IsIdentifier()) + else if (!IsIdentifier()) { return false; } @@ -1742,11 +1735,13 @@ public ParenthesizedTypeNode ParseParenthesizedType() public IFunctionOrConstructorTypeNode ParseFunctionOrConstructorType(SyntaxKind kind) { - var node = kind == SyntaxKind.FunctionType ? - (IFunctionOrConstructorTypeNode)new FunctionTypeNode { Kind = SyntaxKind.FunctionType } : - kind == SyntaxKind.ConstructorType ? - new ConstructorTypeNode { Kind = SyntaxKind.ConstructorType } : - throw new NotSupportedException("parseFunctionOrConstructorType"); + IFunctionOrConstructorTypeNode node = kind switch + { + SyntaxKind.FunctionType => new FunctionTypeNode { Kind = SyntaxKind.FunctionType }, + SyntaxKind.ConstructorType => new ConstructorTypeNode { Kind = SyntaxKind.ConstructorType }, + _ => throw new NotSupportedException("parseFunctionOrConstructorType"), + }; + node.Pos = Scanner.StartPos; //new FunctionOrConstructorTypeNode { kind = kind, pos = scanner.StartPos }; if (kind == SyntaxKind.ConstructorType) @@ -1894,10 +1889,14 @@ public ITypeNode ParseUnionOrIntersectionType(SyntaxKind kind, Func p types.Add(parseConstituentType()); } types.End = GetNodeEnd(); - var node = kind == SyntaxKind.UnionType ? - (IUnionOrIntersectionTypeNode)new UnionTypeNode { Kind = kind, Pos = type.Pos } : - kind == SyntaxKind.IntersectionType ? new IntersectionTypeNode { Kind = kind, Pos = type.Pos } - : throw new NotSupportedException("parseUnionOrIntersectionType"); + + IUnionOrIntersectionTypeNode node = kind switch + { + SyntaxKind.UnionType => new UnionTypeNode { Kind = kind, Pos = type.Pos }, + SyntaxKind.IntersectionType => new IntersectionTypeNode { Kind = kind, Pos = type.Pos }, + _ => throw new NotSupportedException("parseUnionOrIntersectionType"), + }; + node.Types = types; type = FinishNode(node); } @@ -2072,7 +2071,7 @@ public IExpression ParseExpression() } var expr = ParseAssignmentExpressionOrHigher(); - Token operatorToken = null; + Token operatorToken; while ((operatorToken = (Token)ParseOptionalToken(SyntaxKind.CommaToken)) != null) { expr = MakeBinaryExpression(expr, operatorToken, ParseAssignmentExpressionOrHigher()); @@ -2086,14 +2085,11 @@ public IExpression ParseExpression() public IExpression ParseInitializer(bool inParameter) { - if (CurrentToken != SyntaxKind.EqualsToken) + if (CurrentToken != SyntaxKind.EqualsToken && (Scanner.HasPrecedingLineBreak || (inParameter && CurrentToken == SyntaxKind.OpenBraceToken) || !IsStartOfExpression())) { - if (Scanner.HasPrecedingLineBreak || (inParameter && CurrentToken == SyntaxKind.OpenBraceToken) || !IsStartOfExpression()) - { - // preceding line break, open brace in a parameter (likely a function body) or current token is not an expression - - // do not try to parse initializer - return null; - } + // preceding line break, open brace in a parameter (likely a function body) or current token is not an expression - + // do not try to parse initializer + return null; } // Initializer[In, Yield] : // = AssignmentExpression[?In, ?Yield] @@ -2184,7 +2180,7 @@ public YieldExpression ParseYieldExpression() public ArrowFunction ParseSimpleArrowFunctionExpression(Identifier identifier, NodeArray asyncModifier = null) { Debug.Assert(CurrentToken == SyntaxKind.EqualsGreaterThanToken, "parseSimpleArrowFunctionExpression should only have been called if we had a =>"); - ArrowFunction node = null; + ArrowFunction node; if (asyncModifier != null) { node = new ArrowFunction @@ -2454,10 +2450,10 @@ public IExpression ParseConditionalExpressionRest(IExpression leftOperand) public IExpression ParseBinaryExpressionOrHigher(int precedence) { var leftOperand = ParseUnaryExpressionOrHigher(); - return leftOperand == null ? throw new NullReferenceException() : ParseBinaryExpressionRest(precedence, leftOperand); + return ParseBinaryExpressionRest(precedence, leftOperand); } - public bool IsInOrOfKeyword(SyntaxKind t) => t is SyntaxKind.InKeyword or SyntaxKind.OfKeyword; + public static bool IsInOrOfKeyword(SyntaxKind token) => token is SyntaxKind.InKeyword or SyntaxKind.OfKeyword; public IExpression ParseBinaryExpressionRest(int precedence, IExpression leftOperand) { @@ -2696,8 +2692,7 @@ public IExpression ParseIncrementExpression() node.Operand = ParseLeftHandSideExpressionOrHigher(); return FinishNode(node); } - else - if (SourceFile.LanguageVariant == LanguageVariant.Jsx && CurrentToken == SyntaxKind.LessThanToken && LookAhead(NextTokenIsIdentifierOrKeyword)) + else if (SourceFile.LanguageVariant == LanguageVariant.Jsx && CurrentToken == SyntaxKind.LessThanToken && LookAhead(NextTokenIsIdentifierOrKeyword)) { // JSXElement is part of primaryExpression return ParseJsxElementOrSelfClosingElement(true); @@ -2751,7 +2746,7 @@ public IMemberExpression ParseSuperExpression() return FinishNode(node); } - public bool TagNamesAreEquivalent(IJsxTagNameExpression lhs, IJsxTagNameExpression rhs) + public static bool TagNamesAreEquivalent(IJsxTagNameExpression lhs, IJsxTagNameExpression rhs) { if (lhs.Kind != rhs.Kind) { @@ -2769,7 +2764,7 @@ public bool TagNamesAreEquivalent(IJsxTagNameExpression lhs, IJsxTagNameExpressi // take forms of JsxTagNameExpression which includes an identifier, "this" expression, or another propertyAccessExpression // it is safe to case the expression property as such. See parseJsxElementName for how we parse tag name in Jsx element return true; - //todo + //TODO: //((PropertyAccessExpression)lhs).name.text == ((PropertyAccessExpression)rhs).name.text && //tagNamesAreEquivalent(((PropertyAccessExpression)lhs).expression as JsxTagNameExpression, ((PropertyAccessExpression)rhs).expression as JsxTagNameExpression); } @@ -2789,7 +2784,7 @@ public PrimaryExpression ParseJsxElementOrSelfClosingElement(bool inExpressionCo tn ??= (opening as JsxSelfClosingElement)?.TagName; node.JsxChildren = ParseJsxChildren(tn); // IJsxTagNameExpression); node.ClosingElement = ParseJsxClosingElement(inExpressionContext); - // todo check node.closingElement.tagName as JsxTagNameExpression + // TODO: check node.closingElement.tagName as JsxTagNameExpression if (!TagNamesAreEquivalent(tn, node.ClosingElement.TagName)) { ParseErrorAtPosition(node.ClosingElement.Pos ?? 0, (node.ClosingElement.End ?? 0) - (node.ClosingElement.Pos ?? 0), Diagnostics.Expected_corresponding_JSX_closing_tag_for_0, GetTextOfNodeFromSourceText(SourceText, tn)); @@ -2877,16 +2872,14 @@ public NodeArray ParseJsxChildren(IExpression openingTagName) // Closing tag break; } - else - if (CurrentToken == SyntaxKind.EndOfFileToken) + else if (CurrentToken == SyntaxKind.EndOfFileToken) { // If we hit EOF, issue the error at the tag that lacks the closing element // rather than at the end of the file (which is useless) ParseErrorAtPosition(openingTagName.Pos ?? 0, (openingTagName.End ?? 0) - (openingTagName.Pos ?? 0), Diagnostics.JSX_element_0_has_no_corresponding_closing_tag, GetTextOfNodeFromSourceText(SourceText, openingTagName)); break; } - else - if (CurrentToken == SyntaxKind.ConflictMarkerTrivia) + else if (CurrentToken == SyntaxKind.ConflictMarkerTrivia) { break; } @@ -2957,8 +2950,16 @@ public Expression ParseJsxOpeningOrSelfClosingElement(bool inExpressionContext) public IJsxTagNameExpression ParseJsxElementName() { ScanJsxIdentifier(); - IJsxTagNameExpression expression = CurrentToken == SyntaxKind.ThisKeyword ? - ParseTokenNode(CurrentToken) : ParseIdentifierName(); + + if (CurrentToken == SyntaxKind.ThisKeyword) + { + ParseTokenNode(CurrentToken); + } + else + { + ParseIdentifier(); + } + if (CurrentToken == SyntaxKind.ThisKeyword) { IJsxTagNameExpression expression2 = ParseTokenNode(CurrentToken); @@ -3159,8 +3160,7 @@ public IMemberExpression ParseCallExpressionRest(IMemberExpression expression) expression = FinishNode(callExpr); continue; } - else - if (CurrentToken == SyntaxKind.OpenParenToken) + else if (CurrentToken == SyntaxKind.OpenParenToken) { var callExpr = new CallExpression { @@ -3187,27 +3187,28 @@ public NodeArray ParseTypeArgumentsInExpression() { if (!ParseOptional(SyntaxKind.LessThanToken)) { - return null; + return null!; } var typeArguments = ParseDelimitedList(ParsingContextEnum.TypeArguments, ParseType); if (!ParseExpected(SyntaxKind.GreaterThanToken)) { // If it doesn't have the closing > then it's definitely not an type argument list. - return null; + return null!; } // If we have a '<', then only parse this as a argument list if the type arguments // are complete and we have an open paren. if we don't, rewind and return nothing. return typeArguments != null && CanFollowTypeArgumentsInExpression() ? typeArguments - : null; + : null!; } public bool CanFollowTypeArgumentsInExpression() => CurrentToken switch { - SyntaxKind.OpenParenToken or SyntaxKind.DotToken or SyntaxKind.CloseParenToken or SyntaxKind.CloseBracketToken or SyntaxKind.ColonToken or SyntaxKind.SemicolonToken or SyntaxKind.QuestionToken or SyntaxKind.EqualsEqualsToken or SyntaxKind.EqualsEqualsEqualsToken or SyntaxKind.ExclamationEqualsToken or SyntaxKind.ExclamationEqualsEqualsToken or SyntaxKind.AmpersandAmpersandToken or SyntaxKind.BarBarToken or SyntaxKind.CaretToken or SyntaxKind.AmpersandToken or SyntaxKind.BarToken or SyntaxKind.CloseBraceToken or SyntaxKind.EndOfFileToken => true,// foo - // these cases can't legally follow a type arg list. However, they're not legal - // expressions either. The user is probably in the middle of a generic type. So - // treat it as such. + // foo + // these cases can't legally follow a type arg list. However, they're not legal + // expressions either. The user is probably in the middle of a generic type. So + // treat it as such. + SyntaxKind.OpenParenToken or SyntaxKind.DotToken or SyntaxKind.CloseParenToken or SyntaxKind.CloseBracketToken or SyntaxKind.ColonToken or SyntaxKind.SemicolonToken or SyntaxKind.QuestionToken or SyntaxKind.EqualsEqualsToken or SyntaxKind.EqualsEqualsEqualsToken or SyntaxKind.ExclamationEqualsToken or SyntaxKind.ExclamationEqualsEqualsToken or SyntaxKind.AmpersandAmpersandToken or SyntaxKind.BarBarToken or SyntaxKind.CaretToken or SyntaxKind.AmpersandToken or SyntaxKind.BarToken or SyntaxKind.CloseBraceToken or SyntaxKind.EndOfFileToken => true, _ => false,// Anything else treat as an expression. }; @@ -3273,10 +3274,12 @@ public Expression ParseSpreadElement() return FinishNode(node); } - public IExpression ParseArgumentOrArrayLiteralElement() => CurrentToken == SyntaxKind.DotDotDotToken ? ParseSpreadElement() : - CurrentToken == SyntaxKind.CommaToken - ? new OmittedExpression() { Pos = Scanner.StartPos } - : ParseAssignmentExpressionOrHigher(); + public IExpression ParseArgumentOrArrayLiteralElement() => CurrentToken switch + { + SyntaxKind.DotDotDotToken => ParseSpreadElement(), + SyntaxKind.CommaToken => new OmittedExpression() { Pos = Scanner.StartPos }, + _ => ParseAssignmentExpressionOrHigher() + }; public IExpression ParseArgumentExpression() => DoOutsideOfContext(DisallowInAndDecoratorContext, ParseArgumentOrArrayLiteralElement); @@ -3299,8 +3302,7 @@ public IAccessorDeclaration TryParseAccessorDeclaration(int fullStart, NodeArray { return ParseAccessorDeclaration(SyntaxKind.GetAccessor, fullStart, decorators, modifiers); } - else - if (ParseContextualModifier(SyntaxKind.SetKeyword)) + else if (ParseContextualModifier(SyntaxKind.SetKeyword)) { return ParseAccessorDeclaration(SyntaxKind.SetAccessor, fullStart, decorators, modifiers); } @@ -3397,11 +3399,24 @@ public FunctionExpression ParseFunctionExpression() node.AsteriskToken = (AsteriskToken)ParseOptionalToken(SyntaxKind.AsteriskToken); var isGenerator = node.AsteriskToken != null; var isAsync = (GetModifierFlags(node) & ModifierFlags.Async) != 0; - node.Name = - isGenerator && isAsync ? DoInYieldAndAwaitContext(ParseOptionalIdentifier) : - isGenerator ? DoInYieldContext(ParseOptionalIdentifier) : - isAsync ? DoInAwaitContext(ParseOptionalIdentifier) : - ParseOptionalIdentifier(); + + if (isGenerator && isAsync) + { + node.Name = DoInYieldAndAwaitContext(ParseOptionalIdentifier); + } + else if (isGenerator) + { + node.Name = DoInYieldContext(ParseOptionalIdentifier); + } + else if (isAsync) + { + node.Name = DoInAwaitContext(ParseOptionalIdentifier); + } + else + { + node.Name = ParseOptionalIdentifier(); + } + FillSignature(SyntaxKind.ColonToken, isGenerator, isAsync, false, node); node.Body = ParseFunctionBlock(isGenerator, isAsync, false); if (saveDecoratorContext) @@ -3457,7 +3472,7 @@ public Block ParseBlock(bool ignoreMissingOpenBrace, DiagnosticMessage? diagnost } else { - node.Statements = new NodeArray(); //.Cast().ToList(); createMissingList + node.Statements = []; //.Cast().ToList(); createMissingList } return FinishNode(node); } @@ -3562,8 +3577,7 @@ public Statement ParseForOrForInOrForOfStatement() ParseExpected(SyntaxKind.CloseParenToken); forOrForInOrForOfStatement = forOfStatement; } - else - if (ParseOptional(SyntaxKind.InKeyword)) + else if (ParseOptional(SyntaxKind.InKeyword)) { var forInStatement = new ForInStatement { @@ -3600,7 +3614,13 @@ public Statement ParseForOrForInOrForOfStatement() public IBreakOrContinueStatement ParseBreakOrContinueStatement(SyntaxKind kind) { - var node = kind == SyntaxKind.ContinueStatement ? (IBreakOrContinueStatement)new ContinueStatement { Pos = Scanner.StartPos } : kind == SyntaxKind.BreakStatement ? new BreakStatement { Pos = Scanner.StartPos } : throw new NotSupportedException("parseBreakOrContinueStatement"); + IBreakOrContinueStatement node = kind switch + { + SyntaxKind.ContinueStatement => new ContinueStatement { Pos = Scanner.StartPos }, + SyntaxKind.BreakStatement => new BreakStatement { Pos = Scanner.StartPos }, + _ => throw new NotSupportedException("parseBreakOrContinueStatement"), + }; + ParseExpected(kind == SyntaxKind.BreakStatement ? SyntaxKind.BreakKeyword : SyntaxKind.ContinueKeyword); if (!CanParseSemicolon()) { @@ -4196,7 +4216,13 @@ public IClassElement ParsePropertyOrMethodDeclaration(int fullStart, NodeArray decorators, NodeArray modifiers) { - IAccessorDeclaration node = kind == SyntaxKind.GetAccessor ? new GetAccessorDeclaration() { Kind = kind, Pos = fullStart } : kind == SyntaxKind.SetAccessor ? new SetAccessorDeclaration() { Kind = kind, Pos = fullStart } : throw new NotSupportedException("parseAccessorDeclaration"); + IAccessorDeclaration node = kind switch + { + SyntaxKind.GetAccessor => new GetAccessorDeclaration() { Kind = kind, Pos = fullStart }, + SyntaxKind.SetAccessor => new SetAccessorDeclaration() { Kind = kind, Pos = fullStart }, + _ => throw new NotSupportedException("parseAccessorDeclaration"), + }; + node.Decorators = decorators; node.Modifiers = modifiers; node.Name = ParsePropertyName(); @@ -4205,7 +4231,7 @@ public IAccessorDeclaration ParseAccessorDeclaration(SyntaxKind kind, int fullSt return AddJsDocComment(FinishNode(node)); } - public bool IsClassMemberModifier(SyntaxKind idToken) => idToken switch + public static bool IsClassMemberModifier(SyntaxKind idToken) => idToken switch { SyntaxKind.PublicKeyword or SyntaxKind.PrivateKeyword or SyntaxKind.ProtectedKeyword or SyntaxKind.StaticKeyword or SyntaxKind.ReadonlyKeyword => true, _ => false, @@ -4406,7 +4432,7 @@ public ClassExpression ParseClassExpression() } else { - node.Members = new NodeArray(); // createMissingList(); + node.Members = []; // createMissingList(); } return AddJsDocComment(FinishNode(node)); //return (ClassExpression)parseClassDeclarationOrExpression( @@ -4437,7 +4463,7 @@ public ClassDeclaration ParseClassDeclaration(int fullStart, NodeArray(); // createMissingList(); + node.Members = []; // createMissingList(); } return AddJsDocComment(FinishNode(node)); //return (ClassDeclaration)parseClassDeclarationOrExpression(fullStart, decorators, modifiers, SyntaxKind.ClassDeclaration); @@ -4477,7 +4503,7 @@ public Identifier ParseNameOfClassDeclarationOrExpression() => public bool IsImplementsClause() => CurrentToken == SyntaxKind.ImplementsKeyword && LookAhead(NextTokenIsIdentifierOrKeyword); - public NodeArray ParseHeritageClauses() => IsHeritageClause() ? ParseList(ParsingContextEnum.HeritageClauses, ParseHeritageClause) : null; + public NodeArray ParseHeritageClauses() => IsHeritageClause() ? ParseList(ParsingContextEnum.HeritageClauses, ParseHeritageClause) : null!; public HeritageClause ParseHeritageClause() { @@ -4590,7 +4616,7 @@ public ModuleBlock ParseModuleBlock() } else { - node.Statements = new NodeArray(); // createMissingList(); + node.Statements = []; // createMissingList(); } return FinishNode(node); } @@ -4646,8 +4672,7 @@ public ModuleDeclaration ParseModuleDeclaration(int fullStart, NodeArray referencedFiles = new List(); @@ -5056,12 +5087,13 @@ public void ProcessReferenceComments(SourceFile sourceFile) //sourceFile.checkJsDirective = checkJsDirective; } - public void SetExternalModuleIndicator(SourceFile sourceFile) => sourceFile.ExternalModuleIndicator = sourceFile.Statements.FirstOrDefault(node => - { - return HasModifier(node, ModifierFlags.Export) - || (node.Kind == SyntaxKind.ImportEqualsDeclaration && (node as ImportEqualsDeclaration)?.ModuleReference?.Kind == SyntaxKind.ExternalModuleReference) - || node.Kind is SyntaxKind.ImportDeclaration or SyntaxKind.ExportAssignment or SyntaxKind.ExportDeclaration; - //? node : null; - } -); -} \ No newline at end of file + public static void SetExternalModuleIndicator(SourceFile sourceFile) => sourceFile.ExternalModuleIndicator = sourceFile.Statements.Find(node => + { + return HasModifier(node, ModifierFlags.Export) + || (node.Kind == SyntaxKind.ImportEqualsDeclaration && (node as ImportEqualsDeclaration)?.ModuleReference?.Kind == SyntaxKind.ExternalModuleReference) + || node.Kind is SyntaxKind.ImportDeclaration or SyntaxKind.ExportAssignment or SyntaxKind.ExportDeclaration; + //? node : null; + }); +} + +#pragma warning restore S125 // Sections of code should not be commented out diff --git a/src/Blazor.SourceGenerators/TypeScript/Compiler/Scanner.cs b/src/Blazor.SourceGenerators/TypeScript/Compiler/Scanner.cs index 5e38d3d3..81e7f471 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Compiler/Scanner.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Compiler/Scanner.cs @@ -2,12 +2,13 @@ // Licensed under the MIT License. #nullable disable -using System.Diagnostics; using Blazor.SourceGenerators.TypeScript.Types; using static Blazor.SourceGenerators.TypeScript.Compiler.Core; namespace Blazor.SourceGenerators.TypeScript.Compiler; +#pragma warning disable S125 // Sections of code should not be commented out + internal delegate void ErrorCallback(DiagnosticMessage message, int? length = null); internal sealed class Scanner @@ -151,10 +152,10 @@ internal sealed class Scanner private static readonly int s_mergeConflictMarkerLength = "<<<<<<<".Length; private static readonly Regex s_shebangTriviaRegex = new("/^#!.*/"); - private readonly int[] _unicodeEs3IdentifierStart = { 170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 543, 546, 563, 592, 685, 688, 696, 699, 705, 720, 721, 736, 740, 750, 750, 890, 890, 902, 902, 904, 906, 908, 908, 910, 929, 931, 974, 976, 983, 986, 1011, 1024, 1153, 1164, 1220, 1223, 1224, 1227, 1228, 1232, 1269, 1272, 1273, 1329, 1366, 1369, 1369, 1377, 1415, 1488, 1514, 1520, 1522, 1569, 1594, 1600, 1610, 1649, 1747, 1749, 1749, 1765, 1766, 1786, 1788, 1808, 1808, 1810, 1836, 1920, 1957, 2309, 2361, 2365, 2365, 2384, 2384, 2392, 2401, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2524, 2525, 2527, 2529, 2544, 2545, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, 2674, 2676, 2693, 2699, 2701, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2784, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2870, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 2997, 2999, 3001, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3168, 3169, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3294, 3294, 3296, 3297, 3333, 3340, 3342, 3344, 3346, 3368, 3370, 3385, 3424, 3425, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716, 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3760, 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3805, 3840, 3840, 3904, 3911, 3913, 3946, 3976, 3979, 4096, 4129, 4131, 4135, 4137, 4138, 4176, 4181, 4256, 4293, 4304, 4342, 4352, 4441, 4447, 4514, 4520, 4601, 4608, 4614, 4616, 4678, 4680, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4742, 4744, 4744, 4746, 4749, 4752, 4782, 4784, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4814, 4816, 4822, 4824, 4846, 4848, 4878, 4880, 4880, 4882, 4885, 4888, 4894, 4896, 4934, 4936, 4954, 5024, 5108, 5121, 5740, 5743, 5750, 5761, 5786, 5792, 5866, 6016, 6067, 6176, 6263, 6272, 6312, 7680, 7835, 7840, 7929, 7936, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8319, 8319, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8493, 8495, 8497, 8499, 8505, 8544, 8579, 12293, 12295, 12321, 12329, 12337, 12341, 12344, 12346, 12353, 12436, 12445, 12446, 12449, 12538, 12540, 12542, 12549, 12588, 12593, 12686, 12704, 12727, 13312, 19893, 19968, 40869, 40960, 42124, 44032, 55203, 63744, 64045, 64256, 64262, 64275, 64279, 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65136, 65138, 65140, 65140, 65142, 65276, 65313, 65338, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, }; - private readonly int[] _unicodeEs3IdentifierPart = { 170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 543, 546, 563, 592, 685, 688, 696, 699, 705, 720, 721, 736, 740, 750, 750, 768, 846, 864, 866, 890, 890, 902, 902, 904, 906, 908, 908, 910, 929, 931, 974, 976, 983, 986, 1011, 1024, 1153, 1155, 1158, 1164, 1220, 1223, 1224, 1227, 1228, 1232, 1269, 1272, 1273, 1329, 1366, 1369, 1369, 1377, 1415, 1425, 1441, 1443, 1465, 1467, 1469, 1471, 1471, 1473, 1474, 1476, 1476, 1488, 1514, 1520, 1522, 1569, 1594, 1600, 1621, 1632, 1641, 1648, 1747, 1749, 1756, 1759, 1768, 1770, 1773, 1776, 1788, 1808, 1836, 1840, 1866, 1920, 1968, 2305, 2307, 2309, 2361, 2364, 2381, 2384, 2388, 2392, 2403, 2406, 2415, 2433, 2435, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2492, 2492, 2494, 2500, 2503, 2504, 2507, 2509, 2519, 2519, 2524, 2525, 2527, 2531, 2534, 2545, 2562, 2562, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2620, 2620, 2622, 2626, 2631, 2632, 2635, 2637, 2649, 2652, 2654, 2654, 2662, 2676, 2689, 2691, 2693, 2699, 2701, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2748, 2757, 2759, 2761, 2763, 2765, 2768, 2768, 2784, 2784, 2790, 2799, 2817, 2819, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2870, 2873, 2876, 2883, 2887, 2888, 2891, 2893, 2902, 2903, 2908, 2909, 2911, 2913, 2918, 2927, 2946, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 2997, 2999, 3001, 3006, 3010, 3014, 3016, 3018, 3021, 3031, 3031, 3047, 3055, 3073, 3075, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3134, 3140, 3142, 3144, 3146, 3149, 3157, 3158, 3168, 3169, 3174, 3183, 3202, 3203, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3262, 3268, 3270, 3272, 3274, 3277, 3285, 3286, 3294, 3294, 3296, 3297, 3302, 3311, 3330, 3331, 3333, 3340, 3342, 3344, 3346, 3368, 3370, 3385, 3390, 3395, 3398, 3400, 3402, 3405, 3415, 3415, 3424, 3425, 3430, 3439, 3458, 3459, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3530, 3530, 3535, 3540, 3542, 3542, 3544, 3551, 3570, 3571, 3585, 3642, 3648, 3662, 3664, 3673, 3713, 3714, 3716, 3716, 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3769, 3771, 3773, 3776, 3780, 3782, 3782, 3784, 3789, 3792, 3801, 3804, 3805, 3840, 3840, 3864, 3865, 3872, 3881, 3893, 3893, 3895, 3895, 3897, 3897, 3902, 3911, 3913, 3946, 3953, 3972, 3974, 3979, 3984, 3991, 3993, 4028, 4038, 4038, 4096, 4129, 4131, 4135, 4137, 4138, 4140, 4146, 4150, 4153, 4160, 4169, 4176, 4185, 4256, 4293, 4304, 4342, 4352, 4441, 4447, 4514, 4520, 4601, 4608, 4614, 4616, 4678, 4680, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4742, 4744, 4744, 4746, 4749, 4752, 4782, 4784, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4814, 4816, 4822, 4824, 4846, 4848, 4878, 4880, 4880, 4882, 4885, 4888, 4894, 4896, 4934, 4936, 4954, 4969, 4977, 5024, 5108, 5121, 5740, 5743, 5750, 5761, 5786, 5792, 5866, 6016, 6099, 6112, 6121, 6160, 6169, 6176, 6263, 6272, 6313, 7680, 7835, 7840, 7929, 7936, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8255, 8256, 8319, 8319, 8400, 8412, 8417, 8417, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8493, 8495, 8497, 8499, 8505, 8544, 8579, 12293, 12295, 12321, 12335, 12337, 12341, 12344, 12346, 12353, 12436, 12441, 12442, 12445, 12446, 12449, 12542, 12549, 12588, 12593, 12686, 12704, 12727, 13312, 19893, 19968, 40869, 40960, 42124, 44032, 55203, 63744, 64045, 64256, 64262, 64275, 64279, 64285, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65056, 65059, 65075, 65076, 65101, 65103, 65136, 65138, 65140, 65140, 65142, 65276, 65296, 65305, 65313, 65338, 65343, 65343, 65345, 65370, 65381, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, }; - private readonly int[] _unicodeEs5IdentifierStart = { 170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 880, 884, 886, 887, 890, 893, 902, 902, 904, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1162, 1319, 1329, 1366, 1369, 1369, 1377, 1415, 1488, 1514, 1520, 1522, 1568, 1610, 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775, 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957, 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069, 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2208, 2208, 2210, 2220, 2308, 2361, 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2423, 2425, 2431, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529, 2544, 2545, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929, 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3133, 3133, 3160, 3161, 3168, 3169, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261, 3294, 3294, 3296, 3297, 3313, 3314, 3333, 3340, 3342, 3344, 3346, 3386, 3389, 3389, 3406, 3406, 3424, 3425, 3450, 3455, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716, 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3760, 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3807, 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138, 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198, 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5108, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5872, 5888, 5900, 5902, 5905, 5920, 5937, 5952, 5969, 5984, 5996, 5998, 6000, 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6263, 6272, 6312, 6314, 6314, 6320, 6389, 6400, 6428, 6480, 6509, 6512, 6516, 6528, 6571, 6593, 6599, 6656, 6678, 6688, 6740, 6823, 6823, 6917, 6963, 6981, 6987, 7043, 7072, 7086, 7087, 7098, 7141, 7168, 7203, 7245, 7247, 7258, 7293, 7401, 7404, 7406, 7409, 7413, 7414, 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8493, 8495, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, 11360, 11492, 11499, 11502, 11506, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 11823, 11823, 12293, 12295, 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438, 12445, 12447, 12449, 12538, 12540, 12543, 12549, 12589, 12593, 12686, 12704, 12730, 12784, 12799, 13312, 19893, 19968, 40908, 40960, 42124, 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539, 42560, 42606, 42623, 42647, 42656, 42735, 42775, 42783, 42786, 42888, 42891, 42894, 42896, 42899, 42912, 42922, 43000, 43009, 43011, 43013, 43015, 43018, 43020, 43042, 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259, 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442, 43471, 43471, 43520, 43560, 43584, 43586, 43588, 43595, 43616, 43638, 43642, 43642, 43648, 43695, 43697, 43697, 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714, 43739, 43741, 43744, 43754, 43762, 43764, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43968, 44002, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, }; - private readonly int[] _unicodeEs5IdentifierPart = { 170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 768, 884, 886, 887, 890, 893, 902, 902, 904, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1155, 1159, 1162, 1319, 1329, 1366, 1369, 1369, 1377, 1415, 1425, 1469, 1471, 1471, 1473, 1474, 1476, 1477, 1479, 1479, 1488, 1514, 1520, 1522, 1552, 1562, 1568, 1641, 1646, 1747, 1749, 1756, 1759, 1768, 1770, 1788, 1791, 1791, 1808, 1866, 1869, 1969, 1984, 2037, 2042, 2042, 2048, 2093, 2112, 2139, 2208, 2208, 2210, 2220, 2276, 2302, 2304, 2403, 2406, 2415, 2417, 2423, 2425, 2431, 2433, 2435, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2492, 2500, 2503, 2504, 2507, 2510, 2519, 2519, 2524, 2525, 2527, 2531, 2534, 2545, 2561, 2563, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2620, 2620, 2622, 2626, 2631, 2632, 2635, 2637, 2641, 2641, 2649, 2652, 2654, 2654, 2662, 2677, 2689, 2691, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2748, 2757, 2759, 2761, 2763, 2765, 2768, 2768, 2784, 2787, 2790, 2799, 2817, 2819, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2876, 2884, 2887, 2888, 2891, 2893, 2902, 2903, 2908, 2909, 2911, 2915, 2918, 2927, 2929, 2929, 2946, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3006, 3010, 3014, 3016, 3018, 3021, 3024, 3024, 3031, 3031, 3046, 3055, 3073, 3075, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3133, 3140, 3142, 3144, 3146, 3149, 3157, 3158, 3160, 3161, 3168, 3171, 3174, 3183, 3202, 3203, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3260, 3268, 3270, 3272, 3274, 3277, 3285, 3286, 3294, 3294, 3296, 3299, 3302, 3311, 3313, 3314, 3330, 3331, 3333, 3340, 3342, 3344, 3346, 3386, 3389, 3396, 3398, 3400, 3402, 3406, 3415, 3415, 3424, 3427, 3430, 3439, 3450, 3455, 3458, 3459, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3530, 3530, 3535, 3540, 3542, 3542, 3544, 3551, 3570, 3571, 3585, 3642, 3648, 3662, 3664, 3673, 3713, 3714, 3716, 3716, 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3769, 3771, 3773, 3776, 3780, 3782, 3782, 3784, 3789, 3792, 3801, 3804, 3807, 3840, 3840, 3864, 3865, 3872, 3881, 3893, 3893, 3895, 3895, 3897, 3897, 3902, 3911, 3913, 3948, 3953, 3972, 3974, 3991, 3993, 4028, 4038, 4038, 4096, 4169, 4176, 4253, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4957, 4959, 4992, 5007, 5024, 5108, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5872, 5888, 5900, 5902, 5908, 5920, 5940, 5952, 5971, 5984, 5996, 5998, 6000, 6002, 6003, 6016, 6099, 6103, 6103, 6108, 6109, 6112, 6121, 6155, 6157, 6160, 6169, 6176, 6263, 6272, 6314, 6320, 6389, 6400, 6428, 6432, 6443, 6448, 6459, 6470, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6608, 6617, 6656, 6683, 6688, 6750, 6752, 6780, 6783, 6793, 6800, 6809, 6823, 6823, 6912, 6987, 6992, 7001, 7019, 7027, 7040, 7155, 7168, 7223, 7232, 7241, 7245, 7293, 7376, 7378, 7380, 7414, 7424, 7654, 7676, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8204, 8205, 8255, 8256, 8276, 8276, 8305, 8305, 8319, 8319, 8336, 8348, 8400, 8412, 8417, 8417, 8421, 8432, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8493, 8495, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, 11360, 11492, 11499, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11647, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 11744, 11775, 11823, 11823, 12293, 12295, 12321, 12335, 12337, 12341, 12344, 12348, 12353, 12438, 12441, 12442, 12445, 12447, 12449, 12538, 12540, 12543, 12549, 12589, 12593, 12686, 12704, 12730, 12784, 12799, 13312, 19893, 19968, 40908, 40960, 42124, 42192, 42237, 42240, 42508, 42512, 42539, 42560, 42607, 42612, 42621, 42623, 42647, 42655, 42737, 42775, 42783, 42786, 42888, 42891, 42894, 42896, 42899, 42912, 42922, 43000, 43047, 43072, 43123, 43136, 43204, 43216, 43225, 43232, 43255, 43259, 43259, 43264, 43309, 43312, 43347, 43360, 43388, 43392, 43456, 43471, 43481, 43520, 43574, 43584, 43597, 43600, 43609, 43616, 43638, 43642, 43643, 43648, 43714, 43739, 43741, 43744, 43759, 43762, 43766, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43968, 44010, 44012, 44013, 44016, 44025, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65024, 65039, 65056, 65062, 65075, 65076, 65101, 65103, 65136, 65140, 65142, 65276, 65296, 65305, 65313, 65338, 65343, 65343, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, }; + private readonly int[] _unicodeEs3IdentifierStart = [170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 543, 546, 563, 592, 685, 688, 696, 699, 705, 720, 721, 736, 740, 750, 750, 890, 890, 902, 902, 904, 906, 908, 908, 910, 929, 931, 974, 976, 983, 986, 1011, 1024, 1153, 1164, 1220, 1223, 1224, 1227, 1228, 1232, 1269, 1272, 1273, 1329, 1366, 1369, 1369, 1377, 1415, 1488, 1514, 1520, 1522, 1569, 1594, 1600, 1610, 1649, 1747, 1749, 1749, 1765, 1766, 1786, 1788, 1808, 1808, 1810, 1836, 1920, 1957, 2309, 2361, 2365, 2365, 2384, 2384, 2392, 2401, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2524, 2525, 2527, 2529, 2544, 2545, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, 2674, 2676, 2693, 2699, 2701, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2784, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2870, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 2997, 2999, 3001, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3168, 3169, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3294, 3294, 3296, 3297, 3333, 3340, 3342, 3344, 3346, 3368, 3370, 3385, 3424, 3425, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716, 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3760, 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3805, 3840, 3840, 3904, 3911, 3913, 3946, 3976, 3979, 4096, 4129, 4131, 4135, 4137, 4138, 4176, 4181, 4256, 4293, 4304, 4342, 4352, 4441, 4447, 4514, 4520, 4601, 4608, 4614, 4616, 4678, 4680, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4742, 4744, 4744, 4746, 4749, 4752, 4782, 4784, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4814, 4816, 4822, 4824, 4846, 4848, 4878, 4880, 4880, 4882, 4885, 4888, 4894, 4896, 4934, 4936, 4954, 5024, 5108, 5121, 5740, 5743, 5750, 5761, 5786, 5792, 5866, 6016, 6067, 6176, 6263, 6272, 6312, 7680, 7835, 7840, 7929, 7936, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8319, 8319, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8493, 8495, 8497, 8499, 8505, 8544, 8579, 12293, 12295, 12321, 12329, 12337, 12341, 12344, 12346, 12353, 12436, 12445, 12446, 12449, 12538, 12540, 12542, 12549, 12588, 12593, 12686, 12704, 12727, 13312, 19893, 19968, 40869, 40960, 42124, 44032, 55203, 63744, 64045, 64256, 64262, 64275, 64279, 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65136, 65138, 65140, 65140, 65142, 65276, 65313, 65338, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500,]; + private readonly int[] _unicodeEs3IdentifierPart = [170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 543, 546, 563, 592, 685, 688, 696, 699, 705, 720, 721, 736, 740, 750, 750, 768, 846, 864, 866, 890, 890, 902, 902, 904, 906, 908, 908, 910, 929, 931, 974, 976, 983, 986, 1011, 1024, 1153, 1155, 1158, 1164, 1220, 1223, 1224, 1227, 1228, 1232, 1269, 1272, 1273, 1329, 1366, 1369, 1369, 1377, 1415, 1425, 1441, 1443, 1465, 1467, 1469, 1471, 1471, 1473, 1474, 1476, 1476, 1488, 1514, 1520, 1522, 1569, 1594, 1600, 1621, 1632, 1641, 1648, 1747, 1749, 1756, 1759, 1768, 1770, 1773, 1776, 1788, 1808, 1836, 1840, 1866, 1920, 1968, 2305, 2307, 2309, 2361, 2364, 2381, 2384, 2388, 2392, 2403, 2406, 2415, 2433, 2435, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2492, 2492, 2494, 2500, 2503, 2504, 2507, 2509, 2519, 2519, 2524, 2525, 2527, 2531, 2534, 2545, 2562, 2562, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2620, 2620, 2622, 2626, 2631, 2632, 2635, 2637, 2649, 2652, 2654, 2654, 2662, 2676, 2689, 2691, 2693, 2699, 2701, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2748, 2757, 2759, 2761, 2763, 2765, 2768, 2768, 2784, 2784, 2790, 2799, 2817, 2819, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2870, 2873, 2876, 2883, 2887, 2888, 2891, 2893, 2902, 2903, 2908, 2909, 2911, 2913, 2918, 2927, 2946, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 2997, 2999, 3001, 3006, 3010, 3014, 3016, 3018, 3021, 3031, 3031, 3047, 3055, 3073, 3075, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3134, 3140, 3142, 3144, 3146, 3149, 3157, 3158, 3168, 3169, 3174, 3183, 3202, 3203, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3262, 3268, 3270, 3272, 3274, 3277, 3285, 3286, 3294, 3294, 3296, 3297, 3302, 3311, 3330, 3331, 3333, 3340, 3342, 3344, 3346, 3368, 3370, 3385, 3390, 3395, 3398, 3400, 3402, 3405, 3415, 3415, 3424, 3425, 3430, 3439, 3458, 3459, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3530, 3530, 3535, 3540, 3542, 3542, 3544, 3551, 3570, 3571, 3585, 3642, 3648, 3662, 3664, 3673, 3713, 3714, 3716, 3716, 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3769, 3771, 3773, 3776, 3780, 3782, 3782, 3784, 3789, 3792, 3801, 3804, 3805, 3840, 3840, 3864, 3865, 3872, 3881, 3893, 3893, 3895, 3895, 3897, 3897, 3902, 3911, 3913, 3946, 3953, 3972, 3974, 3979, 3984, 3991, 3993, 4028, 4038, 4038, 4096, 4129, 4131, 4135, 4137, 4138, 4140, 4146, 4150, 4153, 4160, 4169, 4176, 4185, 4256, 4293, 4304, 4342, 4352, 4441, 4447, 4514, 4520, 4601, 4608, 4614, 4616, 4678, 4680, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4742, 4744, 4744, 4746, 4749, 4752, 4782, 4784, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4814, 4816, 4822, 4824, 4846, 4848, 4878, 4880, 4880, 4882, 4885, 4888, 4894, 4896, 4934, 4936, 4954, 4969, 4977, 5024, 5108, 5121, 5740, 5743, 5750, 5761, 5786, 5792, 5866, 6016, 6099, 6112, 6121, 6160, 6169, 6176, 6263, 6272, 6313, 7680, 7835, 7840, 7929, 7936, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8255, 8256, 8319, 8319, 8400, 8412, 8417, 8417, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8493, 8495, 8497, 8499, 8505, 8544, 8579, 12293, 12295, 12321, 12335, 12337, 12341, 12344, 12346, 12353, 12436, 12441, 12442, 12445, 12446, 12449, 12542, 12549, 12588, 12593, 12686, 12704, 12727, 13312, 19893, 19968, 40869, 40960, 42124, 44032, 55203, 63744, 64045, 64256, 64262, 64275, 64279, 64285, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65056, 65059, 65075, 65076, 65101, 65103, 65136, 65138, 65140, 65140, 65142, 65276, 65296, 65305, 65313, 65338, 65343, 65343, 65345, 65370, 65381, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500,]; + private readonly int[] _unicodeEs5IdentifierStart = [170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 880, 884, 886, 887, 890, 893, 902, 902, 904, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1162, 1319, 1329, 1366, 1369, 1369, 1377, 1415, 1488, 1514, 1520, 1522, 1568, 1610, 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775, 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957, 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069, 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2208, 2208, 2210, 2220, 2308, 2361, 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2423, 2425, 2431, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529, 2544, 2545, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929, 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3133, 3133, 3160, 3161, 3168, 3169, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261, 3294, 3294, 3296, 3297, 3313, 3314, 3333, 3340, 3342, 3344, 3346, 3386, 3389, 3389, 3406, 3406, 3424, 3425, 3450, 3455, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716, 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3760, 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3807, 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138, 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198, 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5108, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5872, 5888, 5900, 5902, 5905, 5920, 5937, 5952, 5969, 5984, 5996, 5998, 6000, 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6263, 6272, 6312, 6314, 6314, 6320, 6389, 6400, 6428, 6480, 6509, 6512, 6516, 6528, 6571, 6593, 6599, 6656, 6678, 6688, 6740, 6823, 6823, 6917, 6963, 6981, 6987, 7043, 7072, 7086, 7087, 7098, 7141, 7168, 7203, 7245, 7247, 7258, 7293, 7401, 7404, 7406, 7409, 7413, 7414, 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8493, 8495, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, 11360, 11492, 11499, 11502, 11506, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 11823, 11823, 12293, 12295, 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438, 12445, 12447, 12449, 12538, 12540, 12543, 12549, 12589, 12593, 12686, 12704, 12730, 12784, 12799, 13312, 19893, 19968, 40908, 40960, 42124, 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539, 42560, 42606, 42623, 42647, 42656, 42735, 42775, 42783, 42786, 42888, 42891, 42894, 42896, 42899, 42912, 42922, 43000, 43009, 43011, 43013, 43015, 43018, 43020, 43042, 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259, 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442, 43471, 43471, 43520, 43560, 43584, 43586, 43588, 43595, 43616, 43638, 43642, 43642, 43648, 43695, 43697, 43697, 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714, 43739, 43741, 43744, 43754, 43762, 43764, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43968, 44002, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500,]; + private readonly int[] _unicodeEs5IdentifierPart = [170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 768, 884, 886, 887, 890, 893, 902, 902, 904, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1155, 1159, 1162, 1319, 1329, 1366, 1369, 1369, 1377, 1415, 1425, 1469, 1471, 1471, 1473, 1474, 1476, 1477, 1479, 1479, 1488, 1514, 1520, 1522, 1552, 1562, 1568, 1641, 1646, 1747, 1749, 1756, 1759, 1768, 1770, 1788, 1791, 1791, 1808, 1866, 1869, 1969, 1984, 2037, 2042, 2042, 2048, 2093, 2112, 2139, 2208, 2208, 2210, 2220, 2276, 2302, 2304, 2403, 2406, 2415, 2417, 2423, 2425, 2431, 2433, 2435, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2492, 2500, 2503, 2504, 2507, 2510, 2519, 2519, 2524, 2525, 2527, 2531, 2534, 2545, 2561, 2563, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2620, 2620, 2622, 2626, 2631, 2632, 2635, 2637, 2641, 2641, 2649, 2652, 2654, 2654, 2662, 2677, 2689, 2691, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2748, 2757, 2759, 2761, 2763, 2765, 2768, 2768, 2784, 2787, 2790, 2799, 2817, 2819, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2876, 2884, 2887, 2888, 2891, 2893, 2902, 2903, 2908, 2909, 2911, 2915, 2918, 2927, 2929, 2929, 2946, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3006, 3010, 3014, 3016, 3018, 3021, 3024, 3024, 3031, 3031, 3046, 3055, 3073, 3075, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123, 3125, 3129, 3133, 3140, 3142, 3144, 3146, 3149, 3157, 3158, 3160, 3161, 3168, 3171, 3174, 3183, 3202, 3203, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3260, 3268, 3270, 3272, 3274, 3277, 3285, 3286, 3294, 3294, 3296, 3299, 3302, 3311, 3313, 3314, 3330, 3331, 3333, 3340, 3342, 3344, 3346, 3386, 3389, 3396, 3398, 3400, 3402, 3406, 3415, 3415, 3424, 3427, 3430, 3439, 3450, 3455, 3458, 3459, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3530, 3530, 3535, 3540, 3542, 3542, 3544, 3551, 3570, 3571, 3585, 3642, 3648, 3662, 3664, 3673, 3713, 3714, 3716, 3716, 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743, 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3769, 3771, 3773, 3776, 3780, 3782, 3782, 3784, 3789, 3792, 3801, 3804, 3807, 3840, 3840, 3864, 3865, 3872, 3881, 3893, 3893, 3895, 3895, 3897, 3897, 3902, 3911, 3913, 3948, 3953, 3972, 3974, 3991, 3993, 4028, 4038, 4038, 4096, 4169, 4176, 4253, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4957, 4959, 4992, 5007, 5024, 5108, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5872, 5888, 5900, 5902, 5908, 5920, 5940, 5952, 5971, 5984, 5996, 5998, 6000, 6002, 6003, 6016, 6099, 6103, 6103, 6108, 6109, 6112, 6121, 6155, 6157, 6160, 6169, 6176, 6263, 6272, 6314, 6320, 6389, 6400, 6428, 6432, 6443, 6448, 6459, 6470, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6608, 6617, 6656, 6683, 6688, 6750, 6752, 6780, 6783, 6793, 6800, 6809, 6823, 6823, 6912, 6987, 6992, 7001, 7019, 7027, 7040, 7155, 7168, 7223, 7232, 7241, 7245, 7293, 7376, 7378, 7380, 7414, 7424, 7654, 7676, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8204, 8205, 8255, 8256, 8276, 8276, 8305, 8305, 8319, 8319, 8336, 8348, 8400, 8412, 8417, 8417, 8421, 8432, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8493, 8495, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358, 11360, 11492, 11499, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11647, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 11744, 11775, 11823, 11823, 12293, 12295, 12321, 12335, 12337, 12341, 12344, 12348, 12353, 12438, 12441, 12442, 12445, 12447, 12449, 12538, 12540, 12543, 12549, 12589, 12593, 12686, 12704, 12730, 12784, 12799, 13312, 19893, 19968, 40908, 40960, 42124, 42192, 42237, 42240, 42508, 42512, 42539, 42560, 42607, 42612, 42621, 42623, 42647, 42655, 42737, 42775, 42783, 42786, 42888, 42891, 42894, 42896, 42899, 42912, 42922, 43000, 43047, 43072, 43123, 43136, 43204, 43216, 43225, 43232, 43255, 43259, 43259, 43264, 43309, 43312, 43347, 43360, 43388, 43392, 43456, 43471, 43481, 43520, 43574, 43584, 43597, 43600, 43609, 43616, 43638, 43642, 43643, 43648, 43714, 43739, 43741, 43744, 43759, 43762, 43766, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43968, 44010, 44012, 44013, 44016, 44025, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65024, 65039, 65056, 65062, 65075, 65076, 65101, 65103, 65136, 65140, 65142, 65276, 65296, 65305, 65313, 65338, 65343, 65343, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500,]; internal event ErrorCallback OnError; @@ -170,13 +171,7 @@ internal sealed class Scanner internal bool IsReservedWord => Token is >= TypeScriptSyntaxKind.FirstReservedWord and <= TypeScriptSyntaxKind.LastReservedWord; internal bool IsUnterminated { get; private set; } - internal Scanner( - ScriptTarget languageVersion, - bool skipTrivia, - LanguageVariant languageVariant, - string text, - int start = 0, - int? length = null) + internal Scanner(ScriptTarget languageVersion, bool skipTrivia, LanguageVariant languageVariant, string text, int start = 0, int? length = null) { _languageVersion = languageVersion; _languageVariant = languageVariant; @@ -191,7 +186,7 @@ internal Scanner( internal static bool TokenIsIdentifierOrKeyword(TypeScriptSyntaxKind token) => token >= TypeScriptSyntaxKind.Identifier; - internal bool LookupInUnicodeMap(CharacterCode @char, int[] map) + internal static bool LookupInUnicodeMap(CharacterCode @char, int[] map) { var code = (int)@char; if (code < map[0]) @@ -240,11 +235,11 @@ internal bool IsUnicodeIdentifierPart( internal static string TokenToString(TypeScriptSyntaxKind syntaxKind) => s_textToTokenMap.FirstOrDefault(kvp => kvp.Value == syntaxKind).Key; - internal TypeScriptSyntaxKind StringToToken(string s) => s_textToTokenMap[s]; + internal static TypeScriptSyntaxKind StringToToken(string s) => s_textToTokenMap[s]; - internal List ComputeLineStarts(string text) + internal static List ComputeLineStarts(string text) { - List result = new(); + List result = []; var pos = 0; var lineStart = 0; while (pos < text.Length) @@ -258,7 +253,9 @@ internal List ComputeLineStarts(string text) { pos++; } +#pragma warning disable S907 // "goto" statement should not be used goto linefeed; +#pragma warning restore S907 // "goto" statement should not be used case CharacterCode.LineFeed: linefeed: result.Add(lineStart); lineStart = pos; @@ -277,16 +274,16 @@ internal List ComputeLineStarts(string text) } - internal int GetPositionOfLineAndCharacter(SourceFile sourceFile, int line, int character) => + internal static int GetPositionOfLineAndCharacter(SourceFile sourceFile, int line, int character) => ComputePositionOfLineAndCharacter(GetLineStarts(sourceFile), line, character); - internal int ComputePositionOfLineAndCharacter(int[] lineStarts, int line, int character) => + internal static int ComputePositionOfLineAndCharacter(int[] lineStarts, int line, int character) => lineStarts[line] + character; - internal int[] GetLineStarts(ISourceFileLike sourceFile) => - sourceFile.LineMap = ComputeLineStarts(sourceFile.Text).ToArray(); + internal static int[] GetLineStarts(ISourceFileLike sourceFile) => + sourceFile.LineMap = [.. ComputeLineStarts(sourceFile.Text)]; - internal LineAndCharacter ComputeLineAndCharacterOfPosition(int[] lineStarts, int position) + internal static LineAndCharacter ComputeLineAndCharacterOfPosition(int[] lineStarts, int position) { var lineNumber = BinarySearch(lineStarts, position); if (lineNumber < 0) @@ -309,7 +306,7 @@ internal LineAndCharacter ComputeLineAndCharacterOfPosition(int[] lineStarts, in } - internal LineAndCharacter GetLineAndCharacterOfPosition( + internal static LineAndCharacter GetLineAndCharacterOfPosition( SourceFile sourceFile, int position) => ComputeLineAndCharacterOfPosition( @@ -321,7 +318,7 @@ internal static bool IsWhiteSpace(CharacterCode @char) => internal static bool IsWhiteSpaceSingleLine(CharacterCode @char) => // Note: nextLine is in the Zs space, and should be considered to be a whitespace. // It is explicitly not a line-break as it isn't in the exact set specified by EcmaScript. - @char is CharacterCode.Space or CharacterCode.Tab or CharacterCode.VerticalTab or CharacterCode.FormFeed or CharacterCode.NonBreakingSpace or CharacterCode.NextLine or CharacterCode.Ogham or >= CharacterCode.EnQuad and <= CharacterCode.ZeroWidthSpace or CharacterCode.NarrowNoBreakSpace or CharacterCode.MathematicalSpace or CharacterCode.IdeographicSpace or CharacterCode.ByteOrderMark; + @char is CharacterCode.Space or CharacterCode.Tab or CharacterCode.VerticalTab or CharacterCode.FormFeed or CharacterCode.NonBreakingSpace or CharacterCode.NextLine or CharacterCode.Ogham or (>= CharacterCode.EnQuad and <= CharacterCode.ZeroWidthSpace) or CharacterCode.NarrowNoBreakSpace or CharacterCode.MathematicalSpace or CharacterCode.IdeographicSpace or CharacterCode.ByteOrderMark; internal static bool IsLineBreak(CharacterCode @char) => @@ -337,13 +334,13 @@ internal static bool IsLineBreak(CharacterCode @char) => // breaking characters are treated as white space but not as line terminators. @char is CharacterCode.LineFeed or CharacterCode.CarriageReturn or CharacterCode.LineSeparator or CharacterCode.ParagraphSeparator; - internal bool IsDigit(CharacterCode @char) => + internal static bool IsDigit(CharacterCode @char) => @char is >= CharacterCode._0 and <= CharacterCode._9; internal static bool IsOctalDigit(CharacterCode @char) => @char is >= CharacterCode._0 and <= CharacterCode._7; - internal bool CouldStartTrivia(string text, int pos) + internal static bool CouldStartTrivia(string text, int pos) { var @char = text.CharCodeAt(pos); return @char switch @@ -385,7 +382,9 @@ internal static int SkipTriviaM(string text, int pos, bool stopAfterLineBreak = { pos++; } +#pragma warning disable S907 // "goto" statement should not be used goto linefeed; +#pragma warning restore S907 // "goto" statement should not be used case CharacterCode.LineFeed: linefeed: pos++; if (stopAfterLineBreak) @@ -477,7 +476,7 @@ internal static bool IsConflictMarkerTrivia(string text, int pos) { return false; } - }; + } return ch is CharacterCode.equals || text.CharCodeAt(pos + s_mergeConflictMarkerLength) is CharacterCode.Space; } @@ -514,7 +513,7 @@ internal static int ScanConflictMarkerTrivia(string text, int pos, Action + internal static bool IsShebangTrivia(string text, int _) => // Shebangs check must only be done at the start of the file s_shebangTriviaRegex.Test(text); @@ -551,12 +550,16 @@ internal static U IterateCommentRanges( { pos++; } +#pragma warning disable S907 // "goto" statement should not be used goto linefeed; +#pragma warning restore S907 // "goto" statement should not be used case CharacterCode.LineFeed: linefeed: pos++; if (trailing) { +#pragma warning disable S907 // "goto" statement should not be used goto breakScan; +#pragma warning restore S907 // "goto" statement should not be used } collecting = true; if (hasPendingCommentRange) @@ -607,7 +610,7 @@ internal static U IterateCommentRanges( if (hasPendingCommentRange) { accumulator = callback((pendingPos, pendingEnd, pendingKind, pendingHasTrailingNewLine, state, accumulator)); - if (!reduce && accumulator != null) + if (!reduce && !Equals(accumulator, default(U))) { // If we are not reducing and we have a truthy result, return it. return accumulator; @@ -621,7 +624,9 @@ internal static U IterateCommentRanges( } continue; } +#pragma warning disable S907 // "goto" statement should not be used goto breakScan; +#pragma warning restore S907 // "goto" statement should not be used default: if (ch > CharacterCode.MaxAsciiCharacter && IsWhiteSpace(ch)) { @@ -632,7 +637,9 @@ internal static U IterateCommentRanges( pos++; continue; } +#pragma warning disable S907 // "goto" statement should not be used goto breakScan; +#pragma warning restore S907 // "goto" statement should not be used } } breakScan: @@ -644,7 +651,7 @@ internal static U IterateCommentRanges( } - internal U ForEachLeadingCommentRange( + internal static U ForEachLeadingCommentRange( string text, int pos, Func<(int pos, int end, TypeScriptSyntaxKind kind, bool hasTrailingNewLine, T state, U memo), U> callback, @@ -652,7 +659,7 @@ internal U ForEachLeadingCommentRange( IterateCommentRanges(reduce: false, text, pos, trailing: false, callback, state); - internal U ForEachTrailingCommentRange( + internal static U ForEachTrailingCommentRange( string text, int pos, Func<(int pos, int end, TypeScriptSyntaxKind kind, bool hasTrailingNewLine, T state, U memo), U> callback, @@ -679,7 +686,7 @@ internal static U ReduceEachTrailingCommentRange( internal static List AppendCommentRange( (int pos, int end, TypeScriptSyntaxKind kind, bool hasTrailingNewLine, object state, List comments) callback) { - callback.comments = new List(); + callback.comments = []; var comments = new CommentRange { @@ -702,7 +709,7 @@ internal static List GetLeadingCommentRanges( pos, AppendCommentRange, null, - null) ?? new List(); + null) ?? []; internal static List GetTrailingCommentRanges( string text, int pos) => @@ -711,18 +718,18 @@ internal static List GetTrailingCommentRanges( pos, AppendCommentRange, null, - null) ?? new List(); + null) ?? []; - internal string GetShebang(string text) => s_shebangTriviaRegex.Test(text) + internal static string GetShebang(string text) => s_shebangTriviaRegex.Test(text) ? StringExtensions.Match(s_shebangTriviaRegex, text)[0] : null; internal bool IsIdentifierStart(CharacterCode code, ScriptTarget languageVersion) => - code is >= CharacterCode.A and <= CharacterCode.Z or >= CharacterCode.a and <= CharacterCode.z or CharacterCode.Dollar or CharacterCode._ || + code is (>= CharacterCode.A and <= CharacterCode.Z) or (>= CharacterCode.a and <= CharacterCode.z) or CharacterCode.Dollar or CharacterCode._ || (code > CharacterCode.MaxAsciiCharacter && IsUnicodeIdentifierStart(code, languageVersion)); - internal bool IsIdentifierPart(CharacterCode code, ScriptTarget languageVersion) => code is >= CharacterCode.A and <= CharacterCode.Z or >= CharacterCode.a and <= CharacterCode.z or >= CharacterCode._0 and <= CharacterCode._9 or CharacterCode.Dollar or CharacterCode._ || + internal bool IsIdentifierPart(CharacterCode code, ScriptTarget languageVersion) => code is (>= CharacterCode.A and <= CharacterCode.Z) or (>= CharacterCode.a and <= CharacterCode.z) or (>= CharacterCode._0 and <= CharacterCode._9) or CharacterCode.Dollar or CharacterCode._ || (code > CharacterCode.MaxAsciiCharacter && IsUnicodeIdentifierPart(code, languageVersion)); @@ -738,7 +745,7 @@ internal bool IsIdentifierText(string name, ScriptTarget languageVersion) { return false; } - }; + } return true; } @@ -813,13 +820,11 @@ internal int ScanHexDigits(int minCount, bool scanAsManyAsPossible) { value = (value * 16) + ch - CharacterCode._0; } - else - if (ch is >= CharacterCode.A and <= CharacterCode.F) + else if (ch is >= CharacterCode.A and <= CharacterCode.F) { value = (value * 16) + ch - CharacterCode.A + 10; } - else - if (ch is >= CharacterCode.a and <= CharacterCode.f) + else if (ch is >= CharacterCode.a and <= CharacterCode.f) { value = (value * 16) + ch - CharacterCode.a + 10; } @@ -842,13 +847,13 @@ internal string ScanString(bool allowEscapes = true) { var quote = _text.CharCodeAt(TextPos); TextPos++; - var result = ""; + var result = new StringBuilder(); var start = TextPos; while (true) { if (TextPos >= _end) { - result += _text.SubString(start, TextPos); + result.Append(_text.SubString(start, TextPos)); IsUnterminated = true; Error(Diagnostics.Unterminated_string_literal); break; @@ -856,27 +861,27 @@ internal string ScanString(bool allowEscapes = true) var ch = _text.CharCodeAt(TextPos); if (ch == quote) { - result += _text.SubString(start, TextPos); + result.Append(_text.SubString(start, TextPos)); TextPos++; break; } if (ch is CharacterCode.Backslash && allowEscapes) { - result += _text.SubString(start, TextPos); - result += ScanEscapeSequence(); + result.Append(_text.SubString(start, TextPos)); + result.Append(ScanEscapeSequence()); start = TextPos; continue; } if (IsLineBreak(ch)) { - result += _text.SubString(start, TextPos); + result.Append(_text.SubString(start, TextPos)); IsUnterminated = true; Error(Diagnostics.Unterminated_string_literal); break; } TextPos++; } - return result; + return result.ToString(); } @@ -885,13 +890,13 @@ internal TypeScriptSyntaxKind ScanTemplateAndSetTokenValue() var startedWithBacktick = _text.CharCodeAt(TextPos) is CharacterCode.Backtick; TextPos++; var start = TextPos; - var contents = ""; + var contents = new StringBuilder(); TypeScriptSyntaxKind resultingToken; while (true) { if (TextPos >= _end) { - contents += _text.SubString(start, TextPos); + contents.Append(_text.SubString(start, TextPos)); IsUnterminated = true; Error(Diagnostics.Unterminated_template_literal); resultingToken = startedWithBacktick ? TypeScriptSyntaxKind.NoSubstitutionTemplateLiteral : TypeScriptSyntaxKind.TemplateTail; @@ -900,41 +905,41 @@ internal TypeScriptSyntaxKind ScanTemplateAndSetTokenValue() var currChar = _text.CharCodeAt(TextPos); if (currChar is CharacterCode.Backtick) { - contents += _text.SubString(start, TextPos); + contents.Append(_text.SubString(start, TextPos)); TextPos++; resultingToken = startedWithBacktick ? TypeScriptSyntaxKind.NoSubstitutionTemplateLiteral : TypeScriptSyntaxKind.TemplateTail; break; } if (currChar is CharacterCode.Dollar && TextPos + 1 < _end && _text.CharCodeAt(TextPos + 1) is CharacterCode.OpenBrace) { - contents += _text.SubString(start, TextPos); + contents.Append(_text.SubString(start, TextPos)); TextPos += 2; resultingToken = startedWithBacktick ? TypeScriptSyntaxKind.TemplateHead : TypeScriptSyntaxKind.TemplateMiddle; break; } if (currChar is CharacterCode.Backslash) { - contents += _text.SubString(start, TextPos); - contents += ScanEscapeSequence(); + contents.Append(_text.SubString(start, TextPos)); + contents.Append(ScanEscapeSequence()); start = TextPos; continue; } if (currChar is CharacterCode.CarriageReturn) { - contents += _text.SubString(start, TextPos); + contents.Append(_text.SubString(start, TextPos)); TextPos++; if (TextPos < _end && _text.CharCodeAt(TextPos) is CharacterCode.LineFeed) { TextPos++; } - contents += "\n"; + contents.Append('\n'); start = TextPos; continue; } TextPos++; } - //Debug.assert(resultingToken is not null); - TokenValue = contents; + + TokenValue = contents.ToString(); return resultingToken; } @@ -986,7 +991,9 @@ internal string ScanEscapeSequence() { TextPos++; } +#pragma warning disable S907 // "goto" statement should not be used goto linefeed; +#pragma warning restore S907 // "goto" statement should not be used case CharacterCode.LineFeed: case CharacterCode.LineSeparator: case CharacterCode.ParagraphSeparator: @@ -1022,8 +1029,7 @@ internal string ScanExtendedUnicodeEscape() Error(Diagnostics.Hexadecimal_digit_expected); isInvalidExtendedEscape = true; } - else - if (escapedValue > 0x10FFFF) + else if (escapedValue > 0x10FFFF) { Error(Diagnostics.An_extended_Unicode_escape_value_must_be_between_0x0_and_0x10FFFF_inclusive); isInvalidExtendedEscape = true; @@ -1033,8 +1039,7 @@ internal string ScanExtendedUnicodeEscape() Error(Diagnostics.Unexpected_end_of_text); isInvalidExtendedEscape = true; } - else - if (_text.CharCodeAt(TextPos) is CharacterCode.CloseBrace) + else if (_text.CharCodeAt(TextPos) is CharacterCode.CloseBrace) { // Only swallow the following character up if it's a '}'. TextPos++; @@ -1048,7 +1053,7 @@ internal string ScanExtendedUnicodeEscape() } - internal string Utf16EncodeAsString(int codePoint) + internal static string Utf16EncodeAsString(int codePoint) { Debug.Assert(codePoint is >= 0x0 and <= 0x10FFFF); if (codePoint <= 65535) @@ -1076,7 +1081,7 @@ internal CharacterCode PeekUnicodeEscape() internal string ScanIdentifierParts() { - var result = ""; + var result = new StringBuilder(); var start = TextPos; while (TextPos < _end) { @@ -1085,16 +1090,15 @@ internal string ScanIdentifierParts() { TextPos++; } - else - if (ch is CharacterCode.Backslash) + else if (ch is CharacterCode.Backslash) { ch = PeekUnicodeEscape(); if (!(ch >= 0 && IsIdentifierPart(ch, _languageVersion))) { break; } - result += _text.SubString(start, TextPos); - result += StringExtensions.FromCharCode((int)ch); + result.Append(_text.SubString(start, TextPos)); + result.Append(StringExtensions.FromCharCode((int)ch)); // Valid Unicode escape is always six characters TextPos += 6; start = TextPos; @@ -1104,8 +1108,8 @@ internal string ScanIdentifierParts() break; } } - result += _text.SubString(start, TextPos); - return result; + result.Append(_text.SubString(start, TextPos)); + return result.ToString(); } internal TypeScriptSyntaxKind GetIdentifierToken() @@ -1114,13 +1118,10 @@ internal TypeScriptSyntaxKind GetIdentifierToken() if (len is >= 2 and <= 11) { var ch = TokenValue.CharCodeAt(0); - if (ch is >= CharacterCode.a and <= CharacterCode.z) + if (ch is >= CharacterCode.a and <= CharacterCode.z && s_textToTokenMap.TryGetValue(TokenValue, out var value)) { - if (s_textToTokenMap.ContainsKey(TokenValue)) - { - Token = s_textToTokenMap[TokenValue]; - return Token; - } + Token = value; + return Token; } } Token = TypeScriptSyntaxKind.Identifier; @@ -1203,7 +1204,9 @@ internal TypeScriptSyntaxKind Scan() return Token; } #pragma warning disable CS0162 // Unreachable code detected +#pragma warning disable S907 // "goto" statement should not be used goto space; +#pragma warning restore S907 // "goto" statement should not be used #pragma warning restore CS0162 // Unreachable code detected case CharacterCode.Tab: case CharacterCode.VerticalTab: @@ -1224,7 +1227,9 @@ internal TypeScriptSyntaxKind Scan() return Token; } #pragma warning disable CS0162 // Unreachable code detected +#pragma warning disable S907 // "goto" statement should not be used goto exclamation; +#pragma warning restore S907 // "goto" statement should not be used #pragma warning restore CS0162 // Unreachable code detected case CharacterCode.Exclamation: exclamation: if (_text.CharCodeAt(TextPos + 1) is CharacterCode.equals) @@ -1472,7 +1477,9 @@ internal TypeScriptSyntaxKind Scan() Token = TypeScriptSyntaxKind.NumericLiteral; return Token; } +#pragma warning disable S907 // "goto" statement should not be used goto onethroughnine; +#pragma warning restore S907 // "goto" statement should not be used case CharacterCode._1: case CharacterCode._2: case CharacterCode._3: @@ -1659,7 +1666,9 @@ internal TypeScriptSyntaxKind Scan() if (IsIdentifierStart(@char, _languageVersion)) { TextPos++; +#pragma warning disable S1121 // Assignments should not be made from within sub-expressions while (TextPos < _end && IsIdentifierPart(@char = _text.CharCodeAt(TextPos), _languageVersion)) TextPos++; +#pragma warning restore S1121 // Assignments should not be made from within sub-expressions TokenValue = _text.SubString(TokenPos, TextPos); if (@char is CharacterCode.Backslash) { @@ -1749,30 +1758,27 @@ internal TypeScriptSyntaxKind ReScanSlashToken() } if (inEscape) { + // Parsing an escape character; // reset the flag and just advance to the next char. inEscape = false; } - else - if (ch is CharacterCode.Slash && !inCharacterClass) + else if (ch is CharacterCode.Slash && !inCharacterClass) { // A slash within a character class is permissible, // but in general it signals the end of the regexp literal. p++; break; } - else - if (ch is CharacterCode.OpenBracket) + else if (ch is CharacterCode.OpenBracket) { inCharacterClass = true; } - else - if (ch is CharacterCode.Backslash) + else if (ch is CharacterCode.Backslash) { inEscape = true; } - else - if (ch is CharacterCode.CloseBracket) + else if (ch is CharacterCode.CloseBracket) { inCharacterClass = false; } @@ -1990,7 +1996,7 @@ internal T SpeculationHelper(Func callback, bool isLookahead) var saveTokenValue = TokenValue; var savePrecedingLineBreak = HasPrecedingLineBreak; var result = callback(); - if (result is null || (result is bool && Convert.ToBoolean(result) == false) || isLookahead) + if (result is null || (result is bool && !Convert.ToBoolean(result)) || isLookahead) { TextPos = savePos; StartPos = saveStartPos; @@ -2067,4 +2073,6 @@ internal void SetTextPos(int textPos) HasExtendedUnicodeEscape = false; IsUnterminated = false; } -} \ No newline at end of file +} + +#pragma warning restore S125 // Sections of code should not be commented out \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/TypeScript/Compiler/Ts.cs b/src/Blazor.SourceGenerators/TypeScript/Compiler/Ts.cs index 306967a0..08bbd0a2 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Compiler/Ts.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Compiler/Ts.cs @@ -8,6 +8,8 @@ namespace Blazor.SourceGenerators.TypeScript.Compiler; public sealed class Ts { + private Ts() { } + public static INode VisitNode( Func nodeCallback, INode node) => node != null ? nodeCallback(node) : null; @@ -40,11 +42,19 @@ private static INode VisitNodes( Func nodeArrayCallback = null) { var nodeList = nodes?.ToList(); - return nodeList is not null - ? nodeArrayCallback is null - ? VisitEachNode(nodeCallback, nodeList) - : nodeArrayCallback(nodeList.ToArray()) - : null; + + if (nodeArrayCallback is null && nodeList is not null) + { + return VisitEachNode(nodeCallback, nodeList); + } + else if (nodeArrayCallback is not null) + { + return nodeArrayCallback([.. nodeList]); + } + else + { + return null; + } } public static INode ForEachChild( @@ -451,7 +461,7 @@ TypeScriptSyntaxKind.ImportSpecifier or TypeScriptSyntaxKind.ExternalModuleReference => VisitNode(nodeCallback, (node as ExternalModuleReference)?.Expression), - + TypeScriptSyntaxKind.MissingDeclaration => VisitNodes(node.Decorators, nodeCallback, nodeArrayCallback), diff --git a/src/Blazor.SourceGenerators/TypeScript/Compiler/Utilities.cs b/src/Blazor.SourceGenerators/TypeScript/Compiler/Utilities.cs index 24b140a3..d93bc0be 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Compiler/Utilities.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Compiler/Utilities.cs @@ -70,7 +70,7 @@ TypeScriptSyntaxKind.FunctionExpression or ? GetTrailingCommentRanges(text, node.Pos ?? 0).Concat(GetLeadingCommentRanges(text, node.Pos ?? 0)) : GetLeadingCommentRangesOfNodeFromText(node, text); - commentRanges ??= new List(); + commentRanges ??= []; return commentRanges.Where(comment => text.CharCodeAt((comment.Pos ?? 0) + 1) is CharacterCode.Asterisk && text.CharCodeAt((comment.Pos ?? 0) + 2) is CharacterCode.Asterisk && diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/EmitResult.cs b/src/Blazor.SourceGenerators/TypeScript/Types/EmitResult.cs index c9673225..4c61a684 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/EmitResult.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/EmitResult.cs @@ -1,4 +1,7 @@ -using Blazor.SourceGenerators.TypeScript.Compiler; +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. + +using Blazor.SourceGenerators.TypeScript.Compiler; // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/Enums.cs b/src/Blazor.SourceGenerators/TypeScript/Types/Enums.cs index 52ba5364..b4f82eff 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/Enums.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/Enums.cs @@ -7,6 +7,10 @@ #nullable disable namespace Blazor.SourceGenerators.TypeScript.Types; +#pragma warning disable S2344 // Enumeration type names should not have "Flags" or "Enum" suffixes +#pragma warning disable S4663 // Comments should not be empty +#pragma warning disable S125 // Sections of code should not be commented out + [Flags] public enum NodeFlags { @@ -45,6 +49,8 @@ public enum NodeFlags TypeExcludesFlags = YieldContext | AwaitContext } +#pragma warning disable CA1069 // I valori di enumerazione non devono essere duplicati + public enum TypeScriptSyntaxKind { Unknown, @@ -442,6 +448,8 @@ public enum TypeScriptSyntaxKind LastJsDocTagNode = JsDocLiteralType } +#pragma warning restore CA1069 // I valori di enumerazione non devono essere duplicati + public enum CharacterCode { NullCharacter = 0, @@ -809,6 +817,8 @@ public enum TypeReferenceSerializationKind ObjectType // The TypeReferenceNode resolves to any other type. } +#pragma warning disable CA1069 // I valori di enumerazione non devono essere duplicati + public enum SymbolFlags { None = 0, @@ -898,6 +908,8 @@ public enum SymbolFlags Classifiable = Class | Enum | TypeAlias | Interface | TypeParameter | Module } +#pragma warning restore CA1069 // I valori di enumerazione non devono essere duplicati + public enum CheckFlags { Instantiated = 1 << 0, // Instantiated symbol @@ -1160,6 +1172,8 @@ public enum Extension LastTypeScriptExtension = Dts } +#pragma warning disable CA1069 // I valori di enumerazione non devono essere duplicati + public enum TransformFlags { None = 0, @@ -1257,6 +1271,8 @@ public enum TransformFlags Es2015FunctionSyntaxMask = ContainsCapturedLexicalThis | ContainsDefaultValueAssignments } +#pragma warning restore CA1069 // I valori di enumerazione non devono essere duplicati + public enum EmitFlags { SingleLine = 1 << 0, // The contents of this node should be emitted on a single line. @@ -1339,3 +1355,8 @@ public enum EmitHint IdentifierName, // Emitting an IdentifierName Unspecified // Emitting an otherwise unspecified node } + + +#pragma warning restore S125 // Sections of code should not be commented out +#pragma warning restore S4663 // Comments should not be empty +#pragma warning restore S2344 // Enumeration type names should not have "Flags" or "Enum" suffixes \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/Node.cs b/src/Blazor.SourceGenerators/TypeScript/Types/Node.cs index 8c364fe4..6a342208 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/Node.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/Node.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. #nullable disable -using System.Diagnostics; using Blazor.SourceGenerators.TypeScript.Compiler; namespace Blazor.SourceGenerators.TypeScript.Types; @@ -10,19 +9,22 @@ namespace Blazor.SourceGenerators.TypeScript.Types; [DebuggerDisplay("{SourceText}")] public class Node : TextRange, INode { - public List Children { get; set; } = new(); + public List Children { get; set; } = []; public ITypeScriptAbstractSyntaxTree AbstractSyntaxTree { get; set; } public string RawSourceText => AbstractSyntaxTree.RawSourceText; public string SourceText => GetText().ToString(); - public string Identifier => Kind is TypeScriptSyntaxKind.Identifier - ? GetText().ToString() - : Children.FirstOrDefault(v => v.Kind is TypeScriptSyntaxKind.Identifier) - ?.GetText() - .Trim() - .ToString(); + public string Identifier + { + get + { + if (Kind is TypeScriptSyntaxKind.Identifier) return ((Identifier)this).Text; + if (Children.Find(child => child.Kind is TypeScriptSyntaxKind.Identifier) is Identifier identifier) return identifier.Text; + return GetText().ToString(); + } + } public int ParentId { get; set; } public int Depth { get; set; } diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/ParsedCommandLine.cs b/src/Blazor.SourceGenerators/TypeScript/Types/ParsedCommandLine.cs index 247a6d44..0dac6229 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/ParsedCommandLine.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/ParsedCommandLine.cs @@ -1,4 +1,7 @@ -using Blazor.SourceGenerators.TypeScript.Compiler; +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. + +using Blazor.SourceGenerators.TypeScript.Compiler; // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/SourceFile.cs b/src/Blazor.SourceGenerators/TypeScript/Types/SourceFile.cs index 03229dc1..b48184b6 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/SourceFile.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/SourceFile.cs @@ -1,4 +1,7 @@ -using Blazor.SourceGenerators.TypeScript.Compiler; +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. + +using Blazor.SourceGenerators.TypeScript.Compiler; // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/TransformationResult.cs b/src/Blazor.SourceGenerators/TypeScript/Types/TransformationResult.cs index 3ad417b4..00fff9bf 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/TransformationResult.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/TransformationResult.cs @@ -1,4 +1,7 @@ -using Blazor.SourceGenerators.TypeScript.Compiler; +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. + +using Blazor.SourceGenerators.TypeScript.Compiler; // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. diff --git a/src/Blazor.SourceGenerators/Types/DependentTypeMapper.cs b/src/Blazor.SourceGenerators/Types/DependentTypeMapper.cs index ee55040b..a68e919b 100644 --- a/src/Blazor.SourceGenerators/Types/DependentTypeMapper.cs +++ b/src/Blazor.SourceGenerators/Types/DependentTypeMapper.cs @@ -1,8 +1,6 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. -using System; -using System.Reflection.Metadata; using Blazor.SourceGenerators.TypeScript; using Blazor.SourceGenerators.TypeScript.Types; @@ -33,7 +31,7 @@ public static Dictionary GetDependentTypeMap(string interfaceName) var ast = s_lazyDefaultAst.Value; if (ast is null or { RootNode: null }) { - return new(); + return []; } var rootNode = ast.RootNode; @@ -44,7 +42,7 @@ public static Dictionary GetDependentTypeMap(string interfaceName) if (interfaceNode is null) { - return new Dictionary(); + return []; } var dependentTypes = new Dictionary diff --git a/src/Blazor.SourceGenerators/Types/Primitives.cs b/src/Blazor.SourceGenerators/Types/Primitives.cs new file mode 100644 index 00000000..0df532af --- /dev/null +++ b/src/Blazor.SourceGenerators/Types/Primitives.cs @@ -0,0 +1,72 @@ +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. + +namespace Blazor.SourceGenerators.Types; + +internal class Primitives +{ + private static readonly Dictionary _map = new(StringComparer.OrdinalIgnoreCase) + { + // The JavaScript Number type is a double-precision 64-bit binary format IEEE 754 value + ["number"] = "double", + ["string"] = "string", + ["boolean"] = "bool", + ["enum"] = "enum", + ["Date"] = "DateTime", + ["DOMTimeStamp"] = "long", + ["EpochTimeStamp"] = "long", + ["number | null"] = "double?", + ["number | undefined"] = "double?", + ["string | null"] = "string?", + ["string | undefined"] = "string?", + ["boolean | null"] = "bool?", + ["boolean | undefined"] = "bool?", + ["enum | null"] = "enum?", + ["enum | undefined"] = "enum?", + ["Date | null"] = "DateTime?", + ["Date | undefined"] = "DateTime?", + ["DOMTimeStamp | null"] = "long?", + ["EpochTimeStamp | null"] = "long?", + ["URL | null"] = "Uri?", + ["URL | undefined"] = "Uri?", + ["URL"] = "Uri", + ["URLSearchParams"] = "Uri", + ["URLSearchParams | null"] = "Uri?", + ["URLSearchParams | undefined"] = "Uri?", + ["ArrayBuffer"] = "byte[]", + ["ArrayBuffer | null"] = "byte[]?", + ["ArrayBuffer | undefined"] = "byte[]?", + ["ArrayBufferView"] = "byte[]", + ["ArrayBufferView | null"] = "byte[]?", + ["ArrayBufferView | undefined"] = "byte[]?", + ["Blob"] = "byte[]", + ["Blob | null"] = "byte[]?", + ["Blob | undefined"] = "byte[]?", + ["DataView"] = "byte[]", + ["DataView | null"] = "byte[]?", + ["DataView | undefined"] = "byte[]?", + ["FormData"] = "byte[]", + ["FormData | null"] = "byte[]?", + ["FormData | undefined"] = "byte[]?", + ["ReadableStream"] = "byte[]", + ["ReadableStream | null"] = "byte[]?", + ["ReadableStream | undefined"] = "byte[]?", + ["Uint8Array"] = "byte[]", + ["Uint8Array | null"] = "byte[]?", + ["Uint8Array | undefined"] = "byte[]?", + ["Uint8ClampedArray"] = "byte[]", + ["Uint8ClampedArray | null"] = "byte[]?", + ["Uint8ClampedArray | undefined"] = "byte[]?", + //["Array"] = "[]" + }; + + internal static readonly Primitives Instance = new(); + + internal string this[string typescript] => _map.TryGetValue(typescript, out var csharp) + ? csharp + : typescript; + + internal static bool IsPrimitiveType(string typeScriptType) => + _map.ContainsKey(typeScriptType) || + _map.Values.Any(value => value == typeScriptType); +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Types/TypeMap.cs b/src/Blazor.SourceGenerators/Types/TypeMap.cs deleted file mode 100644 index ec7f552d..00000000 --- a/src/Blazor.SourceGenerators/Types/TypeMap.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) David Pine. All rights reserved. -// Licensed under the MIT License. - -namespace Blazor.SourceGenerators.Types; - -internal static class TypeMap -{ - internal static readonly Primitives PrimitiveTypes = new(); - - internal class Primitives - { - internal static readonly Dictionary _primitiveTypeMap = - new(StringComparer.OrdinalIgnoreCase) - { - // The JavaScript Number type is a double-precision 64-bit binary format IEEE 754 value - ["number"] = "double", - ["string"] = "string", - ["boolean"] = "bool", - ["enum"] = "enum", - ["Date"] = "DateTime", - ["DOMTimeStamp"] = "long", - ["EpochTimeStamp"] = "long", - ["number | null"] = "double?", - ["string | null"] = "string?", - ["boolean | null"] = "bool?", - ["enum | null"] = "enum?", - ["Date | null"] = "DateTime?", - ["DOMTimeStamp | null"] = "long?", - ["EpochTimeStamp | null"] = "long?", - ["URL | null"] = "Uri?", - ["URL | undefined"] = "Uri?", - ["URL"] = "Uri", - ["URLSearchParams"] = "Uri", - ["URLSearchParams | null"] = "Uri?", - ["URLSearchParams | undefined"] = "Uri?", - ["ArrayBuffer"] = "byte[]", - ["ArrayBuffer | null"] = "byte[]?", - ["ArrayBuffer | undefined"] = "byte[]?", - ["ArrayBufferView"] = "byte[]", - ["ArrayBufferView | null"] = "byte[]?", - ["ArrayBufferView | undefined"] = "byte[]?", - ["Blob"] = "byte[]", - ["Blob | null"] = "byte[]?", - ["Blob | undefined"] = "byte[]?", - ["DataView"] = "byte[]", - ["DataView | null"] = "byte[]?", - ["DataView | undefined"] = "byte[]?", - ["FormData"] = "byte[]", - ["FormData | null"] = "byte[]?", - ["FormData | undefined"] = "byte[]?", - ["ReadableStream"] = "byte[]", - ["ReadableStream | null"] = "byte[]?", - ["ReadableStream | undefined"] = "byte[]?", - ["Uint8Array"] = "byte[]", - ["Uint8Array | null"] = "byte[]?", - ["Uint8Array | undefined"] = "byte[]?", - ["Uint8ClampedArray"] = "byte[]", - ["Uint8ClampedArray | null"] = "byte[]?", - ["Uint8ClampedArray | undefined"] = "byte[]?", - //["Array"] = "[]" - }; - - internal bool IsPrimitiveType(string typeScriptType) => - _primitiveTypeMap.ContainsKey(typeScriptType) || - _primitiveTypeMap.Values.Any(value => value == typeScriptType); - - internal string this[string typeScriptType] => - _primitiveTypeMap.TryGetValue(typeScriptType, out var csharpType) ? csharpType : typeScriptType; - } -} diff --git a/src/Blazor.SpeechRecognition.WebAssembly/Blazor.SpeechRecognition.WebAssembly.csproj b/src/Blazor.SpeechRecognition.WebAssembly/Blazor.SpeechRecognition.WebAssembly.csproj index 3108762d..541b8b98 100644 --- a/src/Blazor.SpeechRecognition.WebAssembly/Blazor.SpeechRecognition.WebAssembly.csproj +++ b/src/Blazor.SpeechRecognition.WebAssembly/Blazor.SpeechRecognition.WebAssembly.csproj @@ -1,71 +1,71 @@ - - net7.0 - enable - enable - Source generated JavaScript interop for the browser's speechRecognition API compatible with Blazor WebAssembly. - Copyright © David Pine. All rights reserved. Licensed under the MIT License. - en-US - $([System.DateTime]::Now.ToString(yyyyMMdd)) - $(ClientOfficialVersion) - $(ClientPreviewVersion) - nightly-$(CurrentDate) - preview - $(ClientVersion) - $(ClientVersion)-$(VersionSuffix) - $(ClientVersion) - David Pine - true - Blazor.SpeechRecognition.WebAssembly - A C# source-generated class library implementation of the native browser's speechRecognition API available as IJSInProcessRuntime extension methods. - Blazor.SpeechRecognition.WebAssembly - dotnet;dotnetcore;csharp;blazor;webassembly;wasm;generators;sourcegen;roslyn; - - https://github.com/IEvangelist/blazorators - true - true - false - true - AnyCPU - External - Product - embedded - false - false - Blazor.SpeechRecognition.WebAssembly - NU5125;NU5039; - true - https://github.com/IEvangelist/blazorators - LICENSE - git - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true - README.md - true - logo.png - + + $(DefaultTargetFrameworks) + enable + enable + Source generated JavaScript interop for the browser's speechRecognition API compatible with Blazor WebAssembly. + Copyright © David Pine. All rights reserved. Licensed under the MIT License. + en-US + $([System.DateTime]::Now.ToString(yyyyMMdd)) + $(ClientOfficialVersion) + $(ClientPreviewVersion) + nightly-$(CurrentDate) + preview + $(ClientVersion) + $(ClientVersion)-$(VersionSuffix) + $(ClientVersion) + David Pine + true + Blazor.SpeechRecognition.WebAssembly + A C# source-generated class library implementation of the native browser's speechRecognition API available as IJSInProcessRuntime extension methods. + Blazor.SpeechRecognition.WebAssembly + dotnet;dotnetcore;csharp;blazor;webassembly;wasm;generators;sourcegen;roslyn; + + https://github.com/IEvangelist/blazorators + true + true + false + true + AnyCPU + External + Product + embedded + false + false + Blazor.SpeechRecognition.WebAssembly + NU5125;NU5039; + true + https://github.com/IEvangelist/blazorators + LICENSE + git + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + true + README.md + true + logo.png + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + - - - - - + + + + + diff --git a/src/Blazor.SpeechRecognition.WebAssembly/DefaultSpeechRecognitionService.cs b/src/Blazor.SpeechRecognition.WebAssembly/DefaultSpeechRecognitionService.cs index 88dc1482..750ec41a 100644 --- a/src/Blazor.SpeechRecognition.WebAssembly/DefaultSpeechRecognitionService.cs +++ b/src/Blazor.SpeechRecognition.WebAssembly/DefaultSpeechRecognitionService.cs @@ -3,17 +3,14 @@ namespace Microsoft.JSInterop; -internal sealed class DefaultSpeechRecognitionService : ISpeechRecognitionService +internal sealed class DefaultSpeechRecognitionService(IJSInProcessRuntime javaScript) + : ISpeechRecognitionService { - readonly IJSInProcessRuntime _javaScript; readonly SpeechRecognitionCallbackRegistry _callbackRegistry = new(); IJSInProcessObjectReference? _speechRecognitionModule; SpeechRecognitionSubject? _speechRecognition; - public DefaultSpeechRecognitionService( - IJSInProcessRuntime javaScript) => _javaScript = javaScript; - void InitializeSpeechRecognitionSubject() { if (_speechRecognition is not null) @@ -30,7 +27,7 @@ void InitializeSpeechRecognitionSubject() public async Task InitializeModuleAsync(bool logModuleDetails) { _speechRecognitionModule = - await _javaScript.InvokeAsync( + await javaScript.InvokeAsync( "import", "./_content/Blazor.SpeechRecognition.WebAssembly/blazorators.speechRecognition.js"); diff --git a/src/Blazor.SpeechRecognition/Blazor.SpeechRecognition.csproj b/src/Blazor.SpeechRecognition/Blazor.SpeechRecognition.csproj index 2a290f90..6cfef7f2 100644 --- a/src/Blazor.SpeechRecognition/Blazor.SpeechRecognition.csproj +++ b/src/Blazor.SpeechRecognition/Blazor.SpeechRecognition.csproj @@ -1,71 +1,71 @@ - - net7.0 - enable - enable - Source generated JavaScript interop for the browser's speechRecognition API compatible with Blazor. - Copyright © David Pine. All rights reserved. Licensed under the MIT License. - en-US - $([System.DateTime]::Now.ToString(yyyyMMdd)) - $(ClientOfficialVersion) - $(ClientPreviewVersion) - nightly-$(CurrentDate) - preview - $(ClientVersion) - $(ClientVersion)-$(VersionSuffix) - $(ClientVersion) - David Pine - true - Blazor.SpeechRecognition - A C# source-generated class library implementation of the native browser's speechRecognition API available as IJSRuntime extension methods. - Blazor.SpeechRecognition - dotnet;dotnetcore;csharp;blazor;generators;sourcegen;roslyn; - - https://github.com/IEvangelist/blazorators - true - true - false - true - AnyCPU - External - Product - embedded - false - false - Blazor.SpeechRecognition - NU5125;NU5039; - true - https://github.com/IEvangelist/blazorators - LICENSE - git - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true - README.md - true - logo.png - + + $(DefaultTargetFrameworks) + enable + enable + Source generated JavaScript interop for the browser's speechRecognition API compatible with Blazor. + Copyright © David Pine. All rights reserved. Licensed under the MIT License. + en-US + $([System.DateTime]::Now.ToString(yyyyMMdd)) + $(ClientOfficialVersion) + $(ClientPreviewVersion) + nightly-$(CurrentDate) + preview + $(ClientVersion) + $(ClientVersion)-$(VersionSuffix) + $(ClientVersion) + David Pine + true + Blazor.SpeechRecognition + A C# source-generated class library implementation of the native browser's speechRecognition API available as IJSRuntime extension methods. + Blazor.SpeechRecognition + dotnet;dotnetcore;csharp;blazor;generators;sourcegen;roslyn; + + https://github.com/IEvangelist/blazorators + true + true + false + true + AnyCPU + External + Product + embedded + false + false + Blazor.SpeechRecognition + NU5125;NU5039; + true + https://github.com/IEvangelist/blazorators + LICENSE + git + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + true + README.md + true + logo.png + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + - - - - - + + + + + diff --git a/src/Blazor.SpeechRecognition/DefaultSpeechRecognitionService.cs b/src/Blazor.SpeechRecognition/DefaultSpeechRecognitionService.cs index 0387f4f7..8e0cf9a9 100644 --- a/src/Blazor.SpeechRecognition/DefaultSpeechRecognitionService.cs +++ b/src/Blazor.SpeechRecognition/DefaultSpeechRecognitionService.cs @@ -3,22 +3,19 @@ namespace Microsoft.JSInterop; -internal sealed class DefaultSpeechRecognitionService : ISpeechRecognitionService +internal sealed class DefaultSpeechRecognitionService( + IJSRuntime javaScript) : ISpeechRecognitionService { readonly ConcurrentDictionary> _onStartedCallbackRegistry = new(); readonly ConcurrentDictionary> _onEndedCallbackRegistry = new(); readonly ConcurrentDictionary> _onErrorCallbackRegistry = new(); readonly ConcurrentDictionary> _onResultCallbackRegistry = new(); - readonly Lazy> _speechRecognitionModule; - SpeechRecognitionSubject? _speechRecognition; - - public DefaultSpeechRecognitionService( - IJSRuntime javaScript) => - _speechRecognitionModule = + readonly Lazy> _speechRecognitionModule = new(() => javaScript.InvokeAsync( "import", "./_content/Blazor.SpeechRecognition/blazorators.speechRecognition.js") .AsTask()); + SpeechRecognitionSubject? _speechRecognition; async ValueTask InitializeSpeechRecognitionSubjectAsync() { @@ -73,13 +70,13 @@ public async Task RecognizeSpeechAsync( _onResultCallbackRegistry.Clear(); _onResultCallbackRegistry[key] = onRecognized; - + await module.InvokeVoidAsync( JavaScriptInteropMethodIdentifiers.RecognizeSpeech, DotNetObjectReference.Create(this), language, key, - nameof(OnSpeechRecongizedAsync), + nameof(OnSpeechRecognizedAsync), nameof(OnRecognitionErrorAsync), nameof(OnStartedAsync), nameof(OnEndedAsync)); @@ -110,7 +107,7 @@ public Task OnRecognitionErrorAsync(string key, SpeechRecognitionErrorEvent erro await callback.Invoke(errorEvent).ConfigureAwait(false)); [JSInvokable] - public void OnSpeechRecongizedAsync(string key, string transcript, bool isFinal) => + public void OnSpeechRecognizedAsync(string key, string transcript, bool isFinal) => _speechRecognition?.RecognitionReceived( new SpeechRecognitionResult(key, transcript, isFinal)); diff --git a/src/Blazor.SpeechSynthesis.WebAssembly/Blazor.SpeechSynthesis.WebAssembly.csproj b/src/Blazor.SpeechSynthesis.WebAssembly/Blazor.SpeechSynthesis.WebAssembly.csproj index 97d11246..5474bb46 100644 --- a/src/Blazor.SpeechSynthesis.WebAssembly/Blazor.SpeechSynthesis.WebAssembly.csproj +++ b/src/Blazor.SpeechSynthesis.WebAssembly/Blazor.SpeechSynthesis.WebAssembly.csproj @@ -1,75 +1,75 @@ - - net7.0 - enable - enable - Source generated JavaScript interop for the browser's speechSynthesis API compatible with Blazor WebAssembly. - Copyright © David Pine. All rights reserved. Licensed under the MIT License. - en-US - $([System.DateTime]::Now.ToString(yyyyMMdd)) - $(ClientOfficialVersion) - $(ClientPreviewVersion) - nightly-$(CurrentDate) - preview - $(ClientVersion) - $(ClientVersion)-$(VersionSuffix) - $(ClientVersion) - David Pine - true - Blazor.SpeechSynthesis.WebAssembly - A C# source-generated class library implementation of the native browser's speechSynthesis API available as IJSInProcessRuntime extension methods. - Blazor.SpeechSynthesis.WebAssembly - dotnet;dotnetcore;csharp;blazor;webassembly;wasm;generators;sourcegen;roslyn; - - https://github.com/IEvangelist/blazorators - true - true - false - true - AnyCPU - External - Product - embedded - false - false - Blazor.SpeechSynthesis.WebAssembly - NU5125;NU5039; - true - https://github.com/IEvangelist/blazorators - LICENSE - git - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true - README.md - true - logo.png - + + $(DefaultTargetFrameworks) + enable + enable + Source generated JavaScript interop for the browser's speechSynthesis API compatible with Blazor WebAssembly. + Copyright © David Pine. All rights reserved. Licensed under the MIT License. + en-US + $([System.DateTime]::Now.ToString(yyyyMMdd)) + $(ClientOfficialVersion) + $(ClientPreviewVersion) + nightly-$(CurrentDate) + preview + $(ClientVersion) + $(ClientVersion)-$(VersionSuffix) + $(ClientVersion) + David Pine + true + Blazor.SpeechSynthesis.WebAssembly + A C# source-generated class library implementation of the native browser's speechSynthesis API available as IJSInProcessRuntime extension methods. + Blazor.SpeechSynthesis.WebAssembly + dotnet;dotnetcore;csharp;blazor;webassembly;wasm;generators;sourcegen;roslyn; + + https://github.com/IEvangelist/blazorators + true + true + false + true + AnyCPU + External + Product + embedded + false + false + Blazor.SpeechSynthesis.WebAssembly + NU5125;NU5039; + true + https://github.com/IEvangelist/blazorators + LICENSE + git + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + true + README.md + true + logo.png + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + - - - - + + + + - - - - - + + + + + diff --git a/src/Blazor.SpeechSynthesis/Blazor.SpeechSynthesis.csproj b/src/Blazor.SpeechSynthesis/Blazor.SpeechSynthesis.csproj index 07ad2ef5..fe948d66 100644 --- a/src/Blazor.SpeechSynthesis/Blazor.SpeechSynthesis.csproj +++ b/src/Blazor.SpeechSynthesis/Blazor.SpeechSynthesis.csproj @@ -1,79 +1,75 @@ - - net7.0 - enable - enable - Source generated JavaScript interop for the browser's speechSynthesis API compatible with Blazor. - Copyright © David Pine. All rights reserved. Licensed under the MIT License. - en-US - $([System.DateTime]::Now.ToString(yyyyMMdd)) - $(ClientOfficialVersion) - $(ClientPreviewVersion) - nightly-$(CurrentDate) - preview - $(ClientVersion) - $(ClientVersion)-$(VersionSuffix) - $(ClientVersion) - David Pine - true - Blazor.SpeechSynthesis - A C# source-generated class library implementation of the native browser's speechSynthesis API available as IJSRuntime extension methods. - Blazor.SpeechSynthesis - dotnet;dotnetcore;csharp;blazor;generators;sourcegen;roslyn; - - https://github.com/IEvangelist/blazorators - true - true - false - true - AnyCPU - External - Product - embedded - false - false - Blazor.SpeechSynthesis - NU5125;NU5039; - true - https://github.com/IEvangelist/blazorators - LICENSE - git - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true - README.md - true - logo.png - + + $(DefaultTargetFrameworks) + enable + enable + Source generated JavaScript interop for the browser's speechSynthesis API compatible with Blazor. + Copyright © David Pine. All rights reserved. Licensed under the MIT License. + en-US + $([System.DateTime]::Now.ToString(yyyyMMdd)) + $(ClientOfficialVersion) + $(ClientPreviewVersion) + nightly-$(CurrentDate) + preview + $(ClientVersion) + $(ClientVersion)-$(VersionSuffix) + $(ClientVersion) + David Pine + true + Blazor.SpeechSynthesis + A C# source-generated class library implementation of the native browser's speechSynthesis API available as IJSRuntime extension methods. + Blazor.SpeechSynthesis + dotnet;dotnetcore;csharp;blazor;generators;sourcegen;roslyn; + + https://github.com/IEvangelist/blazorators + true + true + false + true + AnyCPU + External + Product + embedded + false + false + Blazor.SpeechSynthesis + NU5125;NU5039; + true + https://github.com/IEvangelist/blazorators + LICENSE + git + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + true + README.md + true + logo.png + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + - - - - + + + + - - - - - - - - - + + + + + diff --git a/tests/Blazor.ExampleConsumer.EndToEndTests/Blazor.ExampleConsumer.EndToEndTests.csproj b/tests/Blazor.ExampleConsumer.EndToEndTests/Blazor.ExampleConsumer.EndToEndTests.csproj index 897f0aee..5e984317 100644 --- a/tests/Blazor.ExampleConsumer.EndToEndTests/Blazor.ExampleConsumer.EndToEndTests.csproj +++ b/tests/Blazor.ExampleConsumer.EndToEndTests/Blazor.ExampleConsumer.EndToEndTests.csproj @@ -1,31 +1,31 @@  - net7.0 + net8.0 enable enable false - - - + + + - - - PreserveNewest - - + + + PreserveNewest + + - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/Blazor.ExampleConsumer.EndToEndTests/GlobalUsings.cs b/tests/Blazor.ExampleConsumer.EndToEndTests/GlobalUsings.cs index f359d90e..e12eff8e 100644 --- a/tests/Blazor.ExampleConsumer.EndToEndTests/GlobalUsings.cs +++ b/tests/Blazor.ExampleConsumer.EndToEndTests/GlobalUsings.cs @@ -1,6 +1,6 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. -global using Xunit; global using System.Diagnostics; -global using Microsoft.Playwright; \ No newline at end of file +global using Microsoft.Playwright; +global using Xunit; diff --git a/tests/Blazor.SourceGenerators.Tests/AssertStringExtensions.cs b/tests/Blazor.SourceGenerators.Tests/AssertStringExtensions.cs index ae4fa4f0..e17477ec 100644 --- a/tests/Blazor.SourceGenerators.Tests/AssertStringExtensions.cs +++ b/tests/Blazor.SourceGenerators.Tests/AssertStringExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Text.RegularExpressions; +using Blazor.SourceGenerators.Builders; namespace Blazor.SourceGenerators.Tests; diff --git a/tests/Blazor.SourceGenerators.Tests/Blazor.SourceGenerators.Tests.csproj b/tests/Blazor.SourceGenerators.Tests/Blazor.SourceGenerators.Tests.csproj index 7591177e..15eda9c4 100644 --- a/tests/Blazor.SourceGenerators.Tests/Blazor.SourceGenerators.Tests.csproj +++ b/tests/Blazor.SourceGenerators.Tests/Blazor.SourceGenerators.Tests.csproj @@ -1,30 +1,30 @@  - net7.0 + net8.0 enable enable false - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - + + + + diff --git a/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs b/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs index 83ac58d2..81f52ad4 100644 --- a/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs +++ b/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs @@ -11,64 +11,57 @@ public class LibDomParserInterfacesTests [Fact] public void CorrectlyConvertsTypeScriptInterfaceToCSharpClass() { - var text = @"interface MediaKeySystemConfiguration { - audioCapabilities?: MediaKeySystemMediaCapability[]; - distinctiveIdentifier?: MediaKeysRequirement; - initDataTypes?: string[]; - label?: string; - persistentState?: MediaKeysRequirement; - sessionTypes?: string[]; - videoCapabilities?: MediaKeySystemMediaCapability[]; -}"; var sut = TypeDeclarationParser.Default; - var actual = sut.ToObject(text); - var expected = @"#nullable enable -using System.Text.Json.Serialization; + var actual = sut.ToObject("MediaKeySystemConfiguration"); + var expected = """ + #nullable enable + using System.Text.Json.Serialization; -namespace Microsoft.JSInterop; + namespace Microsoft.JSInterop; -/// -/// Source-generated object representing an ideally immutable MediaKeySystemConfiguration value. -/// -public class MediaKeySystemConfiguration -{ - /// - /// Source-generated property representing the MediaKeySystemConfiguration.audioCapabilities value. - /// - [JsonPropertyName(""audioCapabilities"")] - public MediaKeySystemMediaCapability[]? AudioCapabilities { get; set; } = default!; - /// - /// Source-generated property representing the MediaKeySystemConfiguration.distinctiveIdentifier value. - /// - [JsonPropertyName(""distinctiveIdentifier"")] - public string? DistinctiveIdentifier { get; set; } = default!; - /// - /// Source-generated property representing the MediaKeySystemConfiguration.initDataTypes value. - /// - [JsonPropertyName(""initDataTypes"")] - public string[]? InitDataTypes { get; set; } = default!; - /// - /// Source-generated property representing the MediaKeySystemConfiguration.label value. - /// - [JsonPropertyName(""label"")] - public string? Label { get; set; } = default!; - /// - /// Source-generated property representing the MediaKeySystemConfiguration.persistentState value. - /// - [JsonPropertyName(""persistentState"")] - public string? PersistentState { get; set; } = default!; - /// - /// Source-generated property representing the MediaKeySystemConfiguration.sessionTypes value. - /// - [JsonPropertyName(""sessionTypes"")] - public string[]? SessionTypes { get; set; } = default!; - /// - /// Source-generated property representing the MediaKeySystemConfiguration.videoCapabilities value. - /// - [JsonPropertyName(""videoCapabilities"")] - public MediaKeySystemMediaCapability[]? VideoCapabilities { get; set; } = default!; -} -"; + /// + /// Source-generated object representing an ideally immutable MediaKeySystemConfiguration value. + /// + public class MediaKeySystemConfiguration + { + /// + /// Source-generated property representing the MediaKeySystemConfiguration.audioCapabilities value. + /// + [JsonPropertyName("audioCapabilities")] + public MediaKeySystemMediaCapability[]? AudioCapabilities { get; set; } = default!; + /// + /// Source-generated property representing the MediaKeySystemConfiguration.distinctiveIdentifier value. + /// + [JsonPropertyName("distinctiveIdentifier")] + public MediaKeysRequirement? DistinctiveIdentifier { get; set; } = default!; + /// + /// Source-generated property representing the MediaKeySystemConfiguration.initDataTypes value. + /// + [JsonPropertyName("initDataTypes")] + public string[]? InitDataTypes { get; set; } = default!; + /// + /// Source-generated property representing the MediaKeySystemConfiguration.label value. + /// + [JsonPropertyName("label")] + public string? Label { get; set; } = default!; + /// + /// Source-generated property representing the MediaKeySystemConfiguration.persistentState value. + /// + [JsonPropertyName("persistentState")] + public MediaKeysRequirement? PersistentState { get; set; } = default!; + /// + /// Source-generated property representing the MediaKeySystemConfiguration.sessionTypes value. + /// + [JsonPropertyName("sessionTypes")] + public string[]? SessionTypes { get; set; } = default!; + /// + /// Source-generated property representing the MediaKeySystemConfiguration.videoCapabilities value. + /// + [JsonPropertyName("videoCapabilities")] + public MediaKeySystemMediaCapability[]? VideoCapabilities { get; set; } = default!; + } + + """; Assert.NotNull(actual); @@ -83,13 +76,8 @@ public class MediaKeySystemConfiguration [Fact] public void CorrectlyConvertsTypeScriptInterfaceToCSharpExtensionObject() { - var text = @"interface Geolocation { - clearWatch(watchId: number): void; - getCurrentPosition(successCallback: PositionCallback, errorCallback?: PositionErrorCallback | null, options?: PositionOptions): void; - watchPosition(successCallback: PositionCallback, errorCallback?: PositionErrorCallback | null, options?: PositionOptions): number; -}"; var sut = TypeDeclarationParser.Default; - var actual = sut.ToTopLevelObject(text); + var actual = sut.ToTopLevelObject("Geolocation"); Assert.NotNull(actual); diff --git a/tests/Blazor.SourceGenerators.Tests/LibDomParserTests.cs b/tests/Blazor.SourceGenerators.Tests/LibDomParserTests.cs index 4bdf6c4a..97cdadbb 100644 --- a/tests/Blazor.SourceGenerators.Tests/LibDomParserTests.cs +++ b/tests/Blazor.SourceGenerators.Tests/LibDomParserTests.cs @@ -34,18 +34,18 @@ public void FindsInterfaceAndCorrespondingImplementationCorrectly() [Fact] public void CanReplaceBruteForceParser() { - var cacheStorage = - _sut.RootNode.OfKind(TypeScriptSyntaxKind.InterfaceDeclaration) - .Single(c => c is { Identifier: "CacheStorage" }); + var cacheStorage = _sut.RootNode.OfKind(TypeScriptSyntaxKind.InterfaceDeclaration) + .Single(type => type is { Identifier: "CacheStorage" }); Assert.NotNull(cacheStorage); - var methods = cacheStorage.OfKind(TypeScriptSyntaxKind.MethodSignature).Cast(); - Assert.Contains(methods, m => m.Identifier is "has"); - Assert.Contains(methods, m => m.Identifier is "open"); - Assert.Contains(methods, m => m.Identifier is "delete"); - Assert.Contains(methods, m => m.Identifier is "keys"); - Assert.Contains(methods, m => m.Identifier is "match"); + var methods = cacheStorage.OfKind(TypeScriptSyntaxKind.MethodSignature); + Assert.Collection(methods, + method => Assert.Equal("delete", method.Identifier), + method => Assert.Equal("has", method.Identifier), + method => Assert.Equal("keys", method.Identifier), + method => Assert.Equal("match", method.Identifier), + method => Assert.Equal("open", method.Identifier)); } [Fact] @@ -55,35 +55,29 @@ public void AbstractSyntaxTreeParsesCorrectly() TypeScriptSyntaxKind.InterfaceDeclaration); var geolocation = interfaces.Single( - type => type.Identifier is "Geolocation" && - type.Kind is TypeScriptSyntaxKind.InterfaceDeclaration); + type => type is { Identifier: "Geolocation", Kind: TypeScriptSyntaxKind.InterfaceDeclaration }); + Assert.NotNull(geolocation); - Assert.Contains(geolocation.Children, - c => c.Identifier is "getCurrentPosition" && - c.Kind is TypeScriptSyntaxKind.MethodSignature); - Assert.Contains(geolocation.Children, - c => c.Identifier is "watchPosition" && - c.Kind is TypeScriptSyntaxKind.MethodSignature); - Assert.Contains(geolocation.Children, - c => c.Identifier is "clearWatch" && - c.Kind is TypeScriptSyntaxKind.MethodSignature); + + var methods = geolocation.OfKind(TypeScriptSyntaxKind.MethodSignature); + Assert.Collection(methods, + method => Assert.Equal("clearWatch", method.Identifier), + method => Assert.Equal("getCurrentPosition", method.Identifier), + method => Assert.Equal("watchPosition", method.Identifier)); var watchPosition = geolocation.Children.Single( c => c.Identifier is "watchPosition"); Assert.NotNull(watchPosition); - Assert.Contains(watchPosition.Children, - c => c.Identifier is "successCallback" && - c.Kind is TypeScriptSyntaxKind.Parameter); - Assert.Contains(watchPosition.Children, - c => c.Identifier is "errorCallback" && - c.Kind is TypeScriptSyntaxKind.Parameter); - Assert.Contains(watchPosition.Children, - c => c.Identifier is "options" && - c.Kind is TypeScriptSyntaxKind.Parameter); - - var successCallback = watchPosition.Children.Single( - c => c.Identifier is "successCallback"); - Assert.NotNull(successCallback); + + var parameters = watchPosition.OfKind(TypeScriptSyntaxKind.Parameter); + Assert.Collection(parameters, + parameter => Assert.Equal("successCallback", parameter.Identifier), + parameter => Assert.Equal("errorCallback", parameter.Identifier), + parameter => Assert.Equal("options", parameter.Identifier)); + + var successCallback = Assert.IsType( + watchPosition.Children.Single(c => c.Identifier is "successCallback")); + Assert.Equal("PositionCallback", successCallback.Type.GetText().ToString().Trim()); } [Fact] @@ -100,10 +94,10 @@ public void ParseStaticObjectCorrectly() var methods = result.Methods; Assert.NotNull(methods); - Assert.Equal(3, methods.Count); - Assert.Contains(methods, m => m.RawName is "clearWatch"); - Assert.Contains(methods, m => m.RawName is "getCurrentPosition"); - Assert.Contains(methods, m => m.RawName is "watchPosition"); + Assert.Collection(methods, + method => Assert.Equal("clearWatch", method.RawName), + method => Assert.Equal("getCurrentPosition", method.RawName), + method => Assert.Equal("watchPosition", method.RawName)); var properties = result.Properties; Assert.NotNull(properties); @@ -111,10 +105,9 @@ public void ParseStaticObjectCorrectly() var dependencies = result.DependentTypes; Assert.NotNull(dependencies); - Assert.Single(dependencies); - Assert.True(dependencies.ContainsKey("PositionOptions")); + Assert.Contains("PositionOptions", dependencies); } - + [Fact] public void VerifyLocalStorageCanBeReadByDefault() { @@ -126,15 +119,16 @@ public void VerifyLocalStorageCanBeReadByDefault() // Assert var properties = parserResult.Value?.Properties; Assert.NotNull(properties); - Assert.Equal(2, properties?.Count ?? 0); + Assert.Collection(properties, + property => Assert.Equal("length", property.RawName)); var methods = parserResult.Value?.Methods; Assert.NotNull(methods); - Assert.Equal(5, methods.Count); - Assert.Contains(methods, m => m.RawName is "getItem"); - Assert.Contains(methods, m => m.RawName is "setItem"); - Assert.Contains(methods, m => m.RawName is "removeItem"); - Assert.Contains(methods, m => m.RawName is "clear"); - Assert.Contains(methods, m => m.RawName is "key"); + Assert.Collection(methods, + method => Assert.Equal("clear", method.RawName), + method => Assert.Equal("getItem", method.RawName), + method => Assert.Equal("key", method.RawName), + method => Assert.Equal("removeItem", method.RawName), + method => Assert.Equal("setItem", method.RawName)); } -} +} \ No newline at end of file diff --git a/tests/Blazor.SourceGenerators.Tests/LibDomReaderTests.cs b/tests/Blazor.SourceGenerators.Tests/LibDomReaderTests.cs index 4dfece14..8503a527 100644 --- a/tests/Blazor.SourceGenerators.Tests/LibDomReaderTests.cs +++ b/tests/Blazor.SourceGenerators.Tests/LibDomReaderTests.cs @@ -15,7 +15,7 @@ public void InitializesTypeDefinitionsCorrectly() var stopwatch = Stopwatch.StartNew(); var sut = TypeDeclarationReader.Default; - _ = sut.TryGetDeclaration("foo", out var _); + _ = sut.TryGetInterface("foo", out var _); stopwatch.Stop(); @@ -30,19 +30,23 @@ public static IEnumerable TryGetDeclarationInput yield return new object[] { "PositionCallback", - @"interface PositionCallback { - (position: GeolocationPosition): void; -}", + """ + interface PositionCallback { + (position: GeolocationPosition): void; + } + """, }; yield return new object[] { "PositionOptions", - @"interface PositionOptions { - enableHighAccuracy?: boolean; - maximumAge?: number; - timeout?: number; -}", + """ + interface PositionOptions { + enableHighAccuracy?: boolean; + maximumAge?: number; + timeout?: number; + } + """, }; } } @@ -54,11 +58,11 @@ public static IEnumerable TryGetDeclarationInput public void TryGetDeclarationReturnsCorrectly(string typeName, string expected) { var sut = TypeDeclarationReader.Default; - var result = sut.TryGetDeclaration(typeName, out var actual); + var result = sut.TryGetInterface(typeName, out var actual); Assert.True(result); Assert.NotNull(actual); - Assert.Equal(expected.NormalizeNewlines(), actual.NormalizeNewlines()); + Assert.Equal(expected.NormalizeNewlines(), actual.GetText().ToString().Trim().NormalizeNewlines()); } public static IEnumerable TryGetTypeAliasInput @@ -84,6 +88,6 @@ public void TryGetTypeAliasReturnsCorrectly(string typeAlias, string expected) Assert.True(result); Assert.NotNull(actual); - Assert.Equal(expected.NormalizeNewlines(), actual.NormalizeNewlines()); + Assert.Equal(expected.NormalizeNewlines(), actual.GetText().ToString().Trim().NormalizeNewlines()); } }