From bb0d294d416d439c4ae0fa4d1813ceb4e073cabe Mon Sep 17 00:00:00 2001 From: David Pine Date: Fri, 13 Oct 2023 10:53:24 -0500 Subject: [PATCH 01/41] Multi-targeting with .NET 8. --- .editorconfig | 1 + .github/FUNDING.yml | 1 - .github/workflows/build-validation.yml | 10 +- .github/workflows/publish-nuget.yml | 6 +- .github/workflows/publish-to-gh-pages.yml | 75 ++++---- Directory.Build.props | 72 +++++--- .../Blazor.ExampleConsumer.csproj | 7 +- .../{ => Components}/App.razor | 0 .../Components/Layout/MainLayout.razor | 19 +++ .../Layout}/MainLayout.razor.css | 160 +++++++++--------- .../Layout}/NavMenu.razor | 0 .../Layout}/NavMenu.razor.css | 124 +++++++------- .../Pages/ClientPosition.razor | 0 .../Pages/ClientPosition.razor.cs | 2 +- .../Pages/Home.razor} | 2 +- .../{ => Components}/Pages/ListenToMe.razor | 0 .../Pages/ListenToMe.razor.cs | 4 +- .../{ => Components}/Pages/ReadToMe.razor | 0 .../{ => Components}/Pages/ReadToMe.razor.cs | 2 +- .../{ => Components}/Pages/Sandbox.razor | 0 .../{ => Components}/Pages/TodoList.razor | 0 .../{ => Components}/Pages/TodoList.razor.cs | 8 +- .../{ => Components}/Pages/Track.razor | 0 .../{ => Components}/Pages/Track.razor.cs | 2 +- .../Components/{ => Shared}/BingMap.razor | 0 .../Components/{ => Shared}/Code.razor | 0 .../Shared/RepositoryPrompt.razor | 0 .../{ => Shared}/StorageCheckbox.razor | 0 .../{ => Components}/_Imports.razor | 4 +- .../Blazor.ExampleConsumer/GlobalUsings.cs | 2 +- .../Blazor.ExampleConsumer/Models/TodoItem.cs | 2 +- samples/Blazor.ExampleConsumer/Program.cs | 5 +- .../Shared/MainLayout.razor | 14 -- .../Blazor.ExampleConsumer/wwwroot/index.html | 1 + .../BlazorServer.ExampleConsumer.csproj | 25 +-- .../GlobalUsings.cs | 8 +- .../Pages/Error.cshtml.cs | 9 +- .../Pages/ListenToMe.razor.cs | 2 +- .../Pages/ReadToMe.razor.cs | 2 - .../Pages/TodoList.razor.cs | 9 +- .../Blazor.Geolocation.WebAssembly.csproj | 8 +- .../Blazor.Geolocation.csproj | 8 +- .../Blazor.LocalStorage.WebAssembly.csproj | 8 +- .../Blazor.LocalStorage.csproj | 8 +- .../Blazor.Permissions.WebAssembly.csproj | 8 +- .../IPermissionsService.cs | 5 +- .../Blazor.Serialization.csproj | 3 +- .../Blazor.SessionStorage.WebAssembly.csproj | 8 +- .../Blazor.SessionStorage.csproj | 8 +- .../AnalyzerReleases.Shipped.md | 3 + .../AnalyzerReleases.Unshipped.md | 11 ++ .../Blazor.SourceGenerators.csproj | 16 +- .../CSharp/CSharpTopLevelObject.cs | 6 +- src/Blazor.SourceGenerators/Compat.cs | 4 +- .../Diagnostics/Descriptors.cs | 4 +- .../GeneratorExecutionContextExtensions.cs | 2 +- .../Extensions/ListExtensions.cs | 2 +- .../GlobalSuppressions.cs | 6 +- .../JavaScriptInteropGenerator.cs | 10 +- .../JavaScriptInteropSyntaxContextReceiver.cs | 2 +- .../TypeDeclarationParser.Interfaces.cs | 2 +- .../Parsers/TypeDeclarationParser.cs | 2 +- .../Readers/TypeDeclarationReader.Factory.cs | 2 - .../TypeDeclarationReader.LocalFiles.cs | 9 - .../Readers/TypeDeclarationReader.cs | 10 +- ...lazor.SpeechRecognition.WebAssembly.csproj | 12 +- .../DefaultSpeechRecognitionService.cs | 9 +- .../Blazor.SpeechRecognition.csproj | 12 +- .../DefaultSpeechRecognitionService.cs | 17 +- .../Blazor.SpeechSynthesis.WebAssembly.csproj | 10 +- .../Blazor.SpeechSynthesis.csproj | 21 ++- ...lazor.ExampleConsumer.EndToEndTests.csproj | 55 +++--- .../GlobalUsings.cs | 4 +- .../Blazor.SourceGenerators.Tests.csproj | 49 +++--- 74 files changed, 471 insertions(+), 451 deletions(-) rename samples/Blazor.ExampleConsumer/{ => Components}/App.razor (100%) create mode 100644 samples/Blazor.ExampleConsumer/Components/Layout/MainLayout.razor rename samples/Blazor.ExampleConsumer/{Shared => Components/Layout}/MainLayout.razor.css (90%) rename samples/Blazor.ExampleConsumer/{Shared => Components/Layout}/NavMenu.razor (100%) rename samples/Blazor.ExampleConsumer/{Shared => Components/Layout}/NavMenu.razor.css (94%) rename samples/Blazor.ExampleConsumer/{ => Components}/Pages/ClientPosition.razor (100%) rename samples/Blazor.ExampleConsumer/{ => Components}/Pages/ClientPosition.razor.cs (96%) rename samples/Blazor.ExampleConsumer/{Pages/Index.razor => Components/Pages/Home.razor} (87%) rename samples/Blazor.ExampleConsumer/{ => Components}/Pages/ListenToMe.razor (100%) rename samples/Blazor.ExampleConsumer/{ => Components}/Pages/ListenToMe.razor.cs (95%) rename samples/Blazor.ExampleConsumer/{ => Components}/Pages/ReadToMe.razor (100%) rename samples/Blazor.ExampleConsumer/{ => Components}/Pages/ReadToMe.razor.cs (98%) rename samples/Blazor.ExampleConsumer/{ => Components}/Pages/Sandbox.razor (100%) rename samples/Blazor.ExampleConsumer/{ => Components}/Pages/TodoList.razor (100%) rename samples/Blazor.ExampleConsumer/{ => Components}/Pages/TodoList.razor.cs (94%) rename samples/Blazor.ExampleConsumer/{ => Components}/Pages/Track.razor (100%) rename samples/Blazor.ExampleConsumer/{ => Components}/Pages/Track.razor.cs (96%) rename samples/Blazor.ExampleConsumer/Components/{ => Shared}/BingMap.razor (100%) rename samples/Blazor.ExampleConsumer/Components/{ => Shared}/Code.razor (100%) rename samples/Blazor.ExampleConsumer/{ => Components}/Shared/RepositoryPrompt.razor (100%) rename samples/Blazor.ExampleConsumer/Components/{ => Shared}/StorageCheckbox.razor (100%) rename samples/Blazor.ExampleConsumer/{ => Components}/_Imports.razor (79%) delete mode 100644 samples/Blazor.ExampleConsumer/Shared/MainLayout.razor create mode 100644 src/Blazor.SourceGenerators/AnalyzerReleases.Shipped.md create mode 100644 src/Blazor.SourceGenerators/AnalyzerReleases.Unshipped.md delete mode 100644 src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.LocalFiles.cs 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..cff91308 100644 --- a/.github/workflows/build-validation.yml +++ b/.github/workflows/build-validation.yml @@ -38,10 +38,11 @@ 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 + dotnet-quality: preview - name: Restore dependencies for ${{ matrix.project }} run: | @@ -59,10 +60,11 @@ 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 + dotnet-quality: preview - name: Run tests run: | diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml index 123224c8..d8607680 100644 --- a/.github/workflows/publish-nuget.yml +++ b/.github/workflows/publish-nuget.yml @@ -12,7 +12,8 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@main with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x + dotnet-quality: preview - name: Test run: dotnet test --filter "Category!=EndToEnd" --configuration Release @@ -65,7 +66,8 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@main with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x + dotnet-quality: preview - name: Restore dependencies for ${{ matrix.project }} run: | diff --git a/.github/workflows/publish-to-gh-pages.yml b/.github/workflows/publish-to-gh-pages.yml index bac5ab3d..6278ba3d 100644 --- a/.github/workflows/publish-to-gh-pages.yml +++ b/.github/workflows/publish-to-gh-pages.yml @@ -1,47 +1,48 @@ -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 + dotnet-quality: preview + - 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..f675dc22 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,30 +1,46 @@ - - <_ParentDirectoryBuildPropsPath Condition="'$(_DirectoryBuildPropsFile)' != ''">$([System.IO.Path]::Combine('..', '$(_DirectoryBuildPropsFile)')) - - - - - - 3 - preview - strict - - - - net7.0 - - - - true - - - - true - - - - true - https://github.com/IEvangelist/azure-cosmos-dotnet-repository - + + <_ParentDirectoryBuildPropsPath Condition="'$(_DirectoryBuildPropsFile)' != ''">$([System.IO.Path]::Combine('..', '$(_DirectoryBuildPropsFile)')) + + + + + + 3 + preview + strict + + + + net7.0;net8.0 + + + + + 7.0.0 + 7.0.0 + 7.0.12 + 7.0.12 + + + + + 8.0.0-rc.2.23479.6 + 8.0.0-rc.2.23479.6 + 8.0.0-rc.2.23480.2 + 8.0.0-rc.2.23480.2 + + + + true + + + + true + + + + true + https://github.com/IEvangelist/azure-cosmos-dotnet-repository + \ No newline at end of file diff --git a/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj b/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj index 8d9b94f9..a836a8db 100644 --- a/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj +++ b/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj @@ -1,15 +1,16 @@  - net7.0 + net8.0 enable enable + preview - - + + 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..a2478125 100644 --- a/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj +++ b/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj @@ -1,22 +1,23 @@ - - net7.0 - enable - enable - + + net8.0 + enable + enable + preview + - - - - - - - + + + + + + +
diff --git a/samples/BlazorServer.ExampleConsumer/GlobalUsings.cs b/samples/BlazorServer.ExampleConsumer/GlobalUsings.cs index 05bd19d0..79b2dbdb 100644 --- a/samples/BlazorServer.ExampleConsumer/GlobalUsings.cs +++ b/samples/BlazorServer.ExampleConsumer/GlobalUsings.cs @@ -3,11 +3,7 @@ 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.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..5a4b77f6 100644 --- a/samples/BlazorServer.ExampleConsumer/Pages/Error.cshtml.cs +++ b/samples/BlazorServer.ExampleConsumer/Pages/Error.cshtml.cs @@ -8,19 +8,12 @@ namespace BlazorServer.ExampleConsumer.Pages; [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] [IgnoreAntiforgeryToken] -public class ErrorModel : PageModel +public class ErrorModel(ILogger logger) : PageModel { public string? RequestId { get; set; } 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..fcb8a4cf 100644 --- a/src/Blazor.Geolocation.WebAssembly/Blazor.Geolocation.WebAssembly.csproj +++ b/src/Blazor.Geolocation.WebAssembly/Blazor.Geolocation.WebAssembly.csproj @@ -4,6 +4,7 @@ $(DefaultTargetFrameworks) enable enable + preview 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 @@ -16,7 +17,7 @@ $(ClientVersion)-$(VersionSuffix) $(ClientVersion) David Pine - + true 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 @@ -56,11 +57,12 @@ 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..fd433f2d 100644 --- a/src/Blazor.Geolocation/Blazor.Geolocation.csproj +++ b/src/Blazor.Geolocation/Blazor.Geolocation.csproj @@ -4,6 +4,7 @@ $(DefaultTargetFrameworks) enable enable + preview 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 @@ -16,7 +17,7 @@ $(ClientVersion)-$(VersionSuffix) $(ClientVersion) David Pine - + true 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 @@ -60,11 +61,12 @@ 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..25455400 100644 --- a/src/Blazor.LocalStorage.WebAssembly/Blazor.LocalStorage.WebAssembly.csproj +++ b/src/Blazor.LocalStorage.WebAssembly/Blazor.LocalStorage.WebAssembly.csproj @@ -4,6 +4,7 @@ $(DefaultTargetFrameworks) enable enable + preview 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 @@ -52,13 +53,12 @@ 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..3b7b9b76 100644 --- a/src/Blazor.LocalStorage/Blazor.LocalStorage.csproj +++ b/src/Blazor.LocalStorage/Blazor.LocalStorage.csproj @@ -4,6 +4,7 @@ $(DefaultTargetFrameworks) enable enable + preview 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 @@ -52,13 +53,12 @@ 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..d5d4e66d 100644 --- a/src/Blazor.Permissions.WebAssembly/Blazor.Permissions.WebAssembly.csproj +++ b/src/Blazor.Permissions.WebAssembly/Blazor.Permissions.WebAssembly.csproj @@ -4,6 +4,7 @@ $(DefaultTargetFrameworks) enable enable + preview 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 @@ -52,13 +53,12 @@ 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..1ae5bdc6 100644 --- a/src/Blazor.Serialization/Blazor.Serialization.csproj +++ b/src/Blazor.Serialization/Blazor.Serialization.csproj @@ -4,6 +4,7 @@ $(DefaultTargetFrameworks) enable enable + preview A C# class library providing light-weight serialization functionality. Copyright © David Pine. All rights reserved. Licensed under the MIT License. en-US @@ -52,7 +53,7 @@ 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..750d097d 100644 --- a/src/Blazor.SessionStorage.WebAssembly/Blazor.SessionStorage.WebAssembly.csproj +++ b/src/Blazor.SessionStorage.WebAssembly/Blazor.SessionStorage.WebAssembly.csproj @@ -4,6 +4,7 @@ $(DefaultTargetFrameworks) enable enable + preview 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 @@ -52,13 +53,12 @@ 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..642ff9a4 100644 --- a/src/Blazor.SessionStorage/Blazor.SessionStorage.csproj +++ b/src/Blazor.SessionStorage/Blazor.SessionStorage.csproj @@ -4,6 +4,7 @@ $(DefaultTargetFrameworks) enable enable + preview 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 @@ -52,13 +53,12 @@ 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..4696b9bc --- /dev/null +++ b/src/Blazor.SourceGenerators/AnalyzerReleases.Unshipped.md @@ -0,0 +1,11 @@ +; 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 \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Blazor.SourceGenerators.csproj b/src/Blazor.SourceGenerators/Blazor.SourceGenerators.csproj index 595e89ea..e127678e 100644 --- a/src/Blazor.SourceGenerators/Blazor.SourceGenerators.csproj +++ b/src/Blazor.SourceGenerators/Blazor.SourceGenerators.csproj @@ -5,9 +5,10 @@ latest enable enable + preview true Copyright © David Pine. All rights reserved. Licensed under the MIT License. - + true true @@ -42,7 +43,7 @@ embedded false false - NU5125;NU5039 + NU5125;NU5039; true https://github.com/IEvangelist/blazorators LICENSE @@ -65,12 +66,11 @@ - - + + - @@ -78,6 +78,10 @@ + + + + @@ -88,7 +92,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs index 8590878b..2cad3a0b 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs @@ -8,9 +8,9 @@ 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); @@ -52,7 +52,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); diff --git a/src/Blazor.SourceGenerators/Compat.cs b/src/Blazor.SourceGenerators/Compat.cs index ce4ff5d4..49b64aac 100644 --- a/src/Blazor.SourceGenerators/Compat.cs +++ b/src/Blazor.SourceGenerators/Compat.cs @@ -1,5 +1,5 @@ -//// Copyright (c) David Pine. All rights reserved. -//// Licensed under the MIT License. +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. using System.ComponentModel; diff --git a/src/Blazor.SourceGenerators/Diagnostics/Descriptors.cs b/src/Blazor.SourceGenerators/Diagnostics/Descriptors.cs index 72364044..97364d43 100644 --- a/src/Blazor.SourceGenerators/Diagnostics/Descriptors.cs +++ b/src/Blazor.SourceGenerators/Diagnostics/Descriptors.cs @@ -24,7 +24,7 @@ static class Descriptors internal static readonly DiagnosticDescriptor UnableToParseGeneratorOptionsDiagnostic = new( "BR0003", "The GeneratorOptions required for source generation are unresolvable", - "JSAutoGenericInteropAttribute must provide the fully qualified 'Descriptors' type name.", + "JSAutoGenericInteropAttribute must provide the fully qualified 'Descriptors' type name", "Blazorators.JSAutoGenericInteropAttribute", DiagnosticSeverity.Error, true); @@ -32,7 +32,7 @@ static class Descriptors internal static readonly DiagnosticDescriptor MissingBlazorSerializationPackageReferenceDiagnostic = new( "BR0004", "Missing package reference of Blazor.Serialization", - "When using JSAutoGenericInteropAttribute you must reference Blazor.Serialization.", + "When using JSAutoGenericInteropAttribute you must reference Blazor.Serialization", "Blazorators.JSAutoGenericInteropAttribute", DiagnosticSeverity.Error, true); diff --git a/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs b/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs index 393438fe..b289efcd 100644 --- a/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs @@ -17,7 +17,7 @@ internal static GeneratorExecutionContext AddDependentTypesSource( SourceText.From(dependentObj.ToString(), Encoding.UTF8)); } - + return context; } diff --git a/src/Blazor.SourceGenerators/Extensions/ListExtensions.cs b/src/Blazor.SourceGenerators/Extensions/ListExtensions.cs index 4afc0e95..d162363c 100644 --- a/src/Blazor.SourceGenerators/Extensions/ListExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/ListExtensions.cs @@ -8,7 +8,7 @@ static class ListExtensions internal static IEnumerable<(Interation Index, T Item)> Select(this List list) { var count = list.Count; - for (var i = 0; i < count; ++ i) + for (var i = 0; i < count; ++i) { yield return (new(i, count), list[i]); } diff --git a/src/Blazor.SourceGenerators/GlobalSuppressions.cs b/src/Blazor.SourceGenerators/GlobalSuppressions.cs index 45ffada9..6e0702e3 100644 --- a/src/Blazor.SourceGenerators/GlobalSuppressions.cs +++ b/src/Blazor.SourceGenerators/GlobalSuppressions.cs @@ -1,7 +1,5 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. using System.Diagnostics.CodeAnalysis; diff --git a/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs b/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs index 451a47e6..c72829b8 100644 --- a/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs +++ b/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs @@ -6,13 +6,13 @@ 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) { @@ -44,7 +44,7 @@ public void Execute(GeneratorExecutionContext context) foreach (var (options, classDeclaration, attribute) in receiver.InterfaceDeclarations) { - if (options is null || IsDiaganosticError(options, context, attribute)) + if (options is null || IsDiagnosticError(options, context, attribute)) { continue; } @@ -90,7 +90,7 @@ public void Execute(GeneratorExecutionContext context) } } - static bool IsDiaganosticError(GeneratorOptions options, GeneratorExecutionContext context, AttributeSyntax attribute) + static bool IsDiagnosticError(GeneratorOptions options, GeneratorExecutionContext context, AttributeSyntax attribute) { if (options.TypeName is null) { diff --git a/src/Blazor.SourceGenerators/JavaScriptInteropSyntaxContextReceiver.cs b/src/Blazor.SourceGenerators/JavaScriptInteropSyntaxContextReceiver.cs index 24b2336c..60f44a2f 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/Parsers/TypeDeclarationParser.Interfaces.cs b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs index 8e7338df..9f8f01b7 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs @@ -359,7 +359,7 @@ internal static string CleanseReturnType(string returnType) string parametersString, Action appendDependentType) { - List parameters = new(); + List parameters = []; // Example input: // "(someCallback: CallbackType, someId?: number | null)" diff --git a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs index 0436bf2d..2921982f 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs @@ -10,7 +10,7 @@ internal sealed partial class TypeDeclarationParser valueFactory: () => new TypeDeclarationParser(TypeDeclarationReader.Default)); readonly TypeDeclarationReader _reader; - + internal static TypeDeclarationParser Default => s_defaultParser.Value; internal TypeDeclarationParser(TypeDeclarationReader reader) => _reader = reader; diff --git a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.Factory.cs b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.Factory.cs index 2279ec32..305301f1 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 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.cs b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs index 214de7ba..0b3002bc 100644 --- a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs +++ b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs @@ -5,7 +5,6 @@ namespace Blazor.SourceGenerators.Readers; internal sealed partial class TypeDeclarationReader { - readonly Uri? _typeDeclarationSource; readonly Lazy _typeDeclarationText; IDictionary? _typeDeclarationMap; @@ -23,17 +22,10 @@ private TypeDeclarationReader() valueFactory: () => GetEmbeddedResourceText()); } - private TypeDeclarationReader(Uri typeDeclarationSource) - { - _typeDeclarationSource = typeDeclarationSource; - _typeDeclarationText = new Lazy( - valueFactory: () => GetLocalFileText(_typeDeclarationSource.LocalPath)); - } - IDictionary ReadTypeDeclarationMap(string typeDeclarations) { ConcurrentDictionary map = new(); - + try { if (typeDeclarations is { Length: > 0 }) diff --git a/src/Blazor.SpeechRecognition.WebAssembly/Blazor.SpeechRecognition.WebAssembly.csproj b/src/Blazor.SpeechRecognition.WebAssembly/Blazor.SpeechRecognition.WebAssembly.csproj index 3108762d..cbf09f2a 100644 --- a/src/Blazor.SpeechRecognition.WebAssembly/Blazor.SpeechRecognition.WebAssembly.csproj +++ b/src/Blazor.SpeechRecognition.WebAssembly/Blazor.SpeechRecognition.WebAssembly.csproj @@ -1,9 +1,10 @@ - net7.0 + $(DefaultTargetFrameworks) enable enable + preview 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 @@ -52,14 +53,13 @@ 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..d939b4ec 100644 --- a/src/Blazor.SpeechRecognition/Blazor.SpeechRecognition.csproj +++ b/src/Blazor.SpeechRecognition/Blazor.SpeechRecognition.csproj @@ -1,9 +1,10 @@ - net7.0 + $(DefaultTargetFrameworks) enable enable + preview 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 @@ -52,14 +53,13 @@ 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..2266d981 100644 --- a/src/Blazor.SpeechSynthesis.WebAssembly/Blazor.SpeechSynthesis.WebAssembly.csproj +++ b/src/Blazor.SpeechSynthesis.WebAssembly/Blazor.SpeechSynthesis.WebAssembly.csproj @@ -1,9 +1,10 @@ - net7.0 + $(DefaultTargetFrameworks) enable enable + preview 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 @@ -52,13 +53,12 @@ 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..6aa82f3b 100644 --- a/src/Blazor.SpeechSynthesis/Blazor.SpeechSynthesis.csproj +++ b/src/Blazor.SpeechSynthesis/Blazor.SpeechSynthesis.csproj @@ -1,9 +1,10 @@ - net7.0 + $(DefaultTargetFrameworks) enable enable + preview 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 @@ -47,18 +48,24 @@ logo.png + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + @@ -72,8 +79,4 @@ - - - - diff --git a/tests/Blazor.ExampleConsumer.EndToEndTests/Blazor.ExampleConsumer.EndToEndTests.csproj b/tests/Blazor.ExampleConsumer.EndToEndTests/Blazor.ExampleConsumer.EndToEndTests.csproj index 897f0aee..f531763c 100644 --- a/tests/Blazor.ExampleConsumer.EndToEndTests/Blazor.ExampleConsumer.EndToEndTests.csproj +++ b/tests/Blazor.ExampleConsumer.EndToEndTests/Blazor.ExampleConsumer.EndToEndTests.csproj @@ -1,34 +1,35 @@  - - net7.0 - enable - enable - false - + + net8.0 + enable + preview + enable + false + - - - + + + - - - PreserveNewest - - + + + PreserveNewest + + - - - - - - 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 + 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/Blazor.SourceGenerators.Tests.csproj b/tests/Blazor.SourceGenerators.Tests/Blazor.SourceGenerators.Tests.csproj index 7591177e..63aa2e64 100644 --- a/tests/Blazor.SourceGenerators.Tests/Blazor.SourceGenerators.Tests.csproj +++ b/tests/Blazor.SourceGenerators.Tests/Blazor.SourceGenerators.Tests.csproj @@ -1,30 +1,31 @@  - - net7.0 - enable - enable - false - + + net8.0 + enable + enable + preview + false + - - - - - - - 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 + all + + - - - - + + + + From 805cfef27e121a06f5b4d445ff226d96639f7a86 Mon Sep 17 00:00:00 2001 From: David Pine Date: Fri, 13 Oct 2023 10:56:53 -0500 Subject: [PATCH 02/41] Fix test path... --- .github/workflows/build-validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-validation.yml b/.github/workflows/build-validation.yml index cff91308..eafb8919 100644 --- a/.github/workflows/build-validation.yml +++ b/.github/workflows/build-validation.yml @@ -76,7 +76,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: | From f240ad3f5e830c699a4f87e7c193154efd503054 Mon Sep 17 00:00:00 2001 From: David Pine Date: Fri, 13 Oct 2023 12:10:44 -0500 Subject: [PATCH 03/41] Gen AI triple slash... --- .../Builders/Indentation.cs | 41 ++++++- .../Builders/IndentationAdjustment.cs | 14 +++ .../Builders/MethodBuilderDetails.cs | 29 ++++- .../Builders/PropertyBuilderDetails.cs | 17 +++ .../Builders/SourceBuilder.cs | 104 +++++++++++------- src/Blazor.SourceGenerators/GlobalUsings.cs | 1 + .../AssertStringExtensions.cs | 8 +- 7 files changed, 169 insertions(+), 45 deletions(-) diff --git a/src/Blazor.SourceGenerators/Builders/Indentation.cs b/src/Blazor.SourceGenerators/Builders/Indentation.cs index bd58c316..86e294f9 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..987a031d 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 { + /// + /// 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..1cbc57f2 100644 --- a/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs +++ b/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs @@ -3,6 +3,21 @@ 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,11 +42,23 @@ internal readonly record struct MethodBuilderDetails( /// internal const string GenericComponentType = "TComponent"; + /// + /// Returns a string representing a generic type argument with the specified value. + /// internal static readonly Func ToGenericTypeArgument = - string (string value) => $"<{value}>"; + 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); diff --git a/src/Blazor.SourceGenerators/Builders/PropertyBuilderDetails.cs b/src/Blazor.SourceGenerators/Builders/PropertyBuilderDetails.cs index a8c4c48b..49802b13 100644 --- a/src/Blazor.SourceGenerators/Builders/PropertyBuilderDetails.cs +++ b/src/Blazor.SourceGenerators/Builders/PropertyBuilderDetails.cs @@ -3,6 +3,17 @@ 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. +/// +/// The to generate code for. +/// The name of the property. +/// The fully qualified JavaScript identifier. +/// The return type of the property. +/// The bare type of the property. +/// The suffix to append to the property name. +/// The type to extend. +/// The generic type arguments. internal readonly record struct PropertyBuilderDetails( CSharpProperty Property, string CSharpPropertyName, @@ -13,6 +24,12 @@ 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(); diff --git a/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs b/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs index a6e78c24..aed6ab50 100644 --- a/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs +++ b/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs @@ -1,15 +1,16 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. -using System.Diagnostics; - 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 +20,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 +75,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 +93,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 +103,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 +126,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 +141,7 @@ internal SourceBuilder AppendOpeningCurlyBrace(bool increaseIndentation = false) { IncreaseIndentationImpl(increaseIndentation); - _builder.Append($"{_indentation}{{{_newLine}"); + _builder.Append($"{_indentation}{{{NewLine}"); return this; } @@ -120,7 +150,7 @@ internal SourceBuilder AppendClosingCurlyBrace(bool decreaseIndentation = false) { DecreaseIndentationImpl(decreaseIndentation); - _builder.Append($"{_indentation}}}{_newLine}"); + _builder.Append($"{_indentation}}}{NewLine}"); return this; } @@ -133,16 +163,16 @@ internal SourceBuilder AppendTripleSlashMethodComments( 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}"); + _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}/// {NewLine}"); + _builder.Append($"{indent}/// {NewLine}"); if (extrapolateParameters) { @@ -151,7 +181,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 +192,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}"); } } } @@ -181,7 +211,7 @@ internal SourceBuilder AppendEmptyTripleSlashInheritdocComments( AdjustIndentation(adjustment); var indent = _indentation.ToString(); - _builder.Append($"{indent}/// {_newLine}"); + _builder.Append($"{indent}/// {NewLine}"); return this; } @@ -194,7 +224,7 @@ internal SourceBuilder AppendTripleSlashInheritdocComments( AdjustIndentation(adjustment); var indent = _indentation.ToString(); - _builder.Append($"{indent}/// {_newLine}"); + _builder.Append($"{indent}/// {NewLine}"); return this; } @@ -206,7 +236,7 @@ internal SourceBuilder AppendTripleSlashPropertyComments( AdjustIndentation(adjustment); var indent = _indentation.ToString(); - _builder.Append($"{indent}/// {_newLine}"); + _builder.Append($"{indent}/// {NewLine}"); var jsMethodName = property.RawName.LowerCaseFirstLetter(); var func = $"{_options.Implementation}.{jsMethodName}"; @@ -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) { diff --git a/src/Blazor.SourceGenerators/GlobalUsings.cs b/src/Blazor.SourceGenerators/GlobalUsings.cs index 681d41a2..3d2461b0 100644 --- a/src/Blazor.SourceGenerators/GlobalUsings.cs +++ b/src/Blazor.SourceGenerators/GlobalUsings.cs @@ -3,6 +3,7 @@ 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; diff --git a/tests/Blazor.SourceGenerators.Tests/AssertStringExtensions.cs b/tests/Blazor.SourceGenerators.Tests/AssertStringExtensions.cs index 3506bbed..d116e7dc 100644 --- a/tests/Blazor.SourceGenerators.Tests/AssertStringExtensions.cs +++ b/tests/Blazor.SourceGenerators.Tests/AssertStringExtensions.cs @@ -2,11 +2,15 @@ // Licensed under the MIT License. using System.Text.RegularExpressions; +using Blazor.SourceGenerators.Builders; namespace Blazor.SourceGenerators.Tests; -static class AssertStringExtensions +static partial class AssertStringExtensions { internal static string NormalizeNewlines(this string value) => - Regex.Replace(value, @"\r\n|\n\r|\n|\r", "\r\n"); + NewLineRegex().Replace(value, SourceBuilder.NewLine.ToString()); + + [GeneratedRegex(@"\r\n|\n\r|\n|\r")] + private static partial Regex NewLineRegex(); } From b55a9c81c765dcf9a7bb04f22936a1abda92333d Mon Sep 17 00:00:00 2001 From: David Pine Date: Fri, 13 Oct 2023 15:37:13 -0500 Subject: [PATCH 04/41] Fix the publish bits. --- .github/workflows/publish-nuget.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml index d8607680..d5969d3d 100644 --- a/.github/workflows/publish-nuget.yml +++ b/.github/workflows/publish-nuget.yml @@ -23,7 +23,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: | From da91a52da552df5b3bed56f12566a26c91d19762 Mon Sep 17 00:00:00 2001 From: David Pine Date: Fri, 13 Oct 2023 15:43:29 -0500 Subject: [PATCH 05/41] Remove legacy switch --- .github/workflows/publish-nuget.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml index d5969d3d..0aae412a 100644 --- a/.github/workflows/publish-nuget.yml +++ b/.github/workflows/publish-nuget.yml @@ -71,7 +71,7 @@ jobs: - 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: | From 407d40ef736439ba5cd39dc62b3d85723fb8c648 Mon Sep 17 00:00:00 2001 From: David Pine Date: Fri, 17 Nov 2023 12:53:55 -0600 Subject: [PATCH 06/41] Upgrade to .NET 8.0, and use Central Package Management. --- .github/workflows/build-validation.yml | 2 - .github/workflows/publish-nuget.yml | 2 - .github/workflows/publish-to-gh-pages.yml | 1 - Directory.Build.props | 73 ++++----- Directory.Packages.props | 41 +++++ blazorators.sln | 1 + .../Blazor.ExampleConsumer.csproj | 37 +++-- .../BlazorServer.ExampleConsumer.csproj | 35 ++--- .../GlobalUsings.cs | 4 +- .../Blazor.Geolocation.WebAssembly.csproj | 140 +++++++++-------- .../Blazor.Geolocation.csproj | 146 +++++++++--------- .../Blazor.LocalStorage.WebAssembly.csproj | 136 ++++++++-------- .../Blazor.LocalStorage.csproj | 134 ++++++++-------- .../Blazor.Permissions.WebAssembly.csproj | 134 ++++++++-------- .../Blazor.Serialization.csproj | 123 ++++++++------- .../Blazor.SessionStorage.WebAssembly.csproj | 136 ++++++++-------- .../Blazor.SessionStorage.csproj | 134 ++++++++-------- .../Blazor.SourceGenerators.csproj | 98 ++++++------ .../TypeDeclarationReader.RemoteFile.cs | 22 --- ...lazor.SpeechRecognition.WebAssembly.csproj | 130 ++++++++-------- .../Blazor.SpeechRecognition.csproj | 130 ++++++++-------- .../Blazor.SpeechSynthesis.WebAssembly.csproj | 136 ++++++++-------- .../Blazor.SpeechSynthesis.csproj | 143 ++++++++--------- ...lazor.ExampleConsumer.EndToEndTests.csproj | 39 +++-- .../Blazor.SourceGenerators.Tests.csproj | 41 +++-- 25 files changed, 998 insertions(+), 1020 deletions(-) create mode 100644 Directory.Packages.props delete mode 100644 src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.RemoteFile.cs diff --git a/.github/workflows/build-validation.yml b/.github/workflows/build-validation.yml index eafb8919..27b8acec 100644 --- a/.github/workflows/build-validation.yml +++ b/.github/workflows/build-validation.yml @@ -42,7 +42,6 @@ jobs: uses: actions/setup-dotnet@main with: dotnet-version: 8.0.x - dotnet-quality: preview - name: Restore dependencies for ${{ matrix.project }} run: | @@ -64,7 +63,6 @@ jobs: uses: actions/setup-dotnet@main with: dotnet-version: 8.0.x - dotnet-quality: preview - name: Run tests run: | diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml index 0aae412a..d5b414d7 100644 --- a/.github/workflows/publish-nuget.yml +++ b/.github/workflows/publish-nuget.yml @@ -13,7 +13,6 @@ jobs: uses: actions/setup-dotnet@main with: dotnet-version: 8.0.x - dotnet-quality: preview - name: Test run: dotnet test --filter "Category!=EndToEnd" --configuration Release @@ -67,7 +66,6 @@ jobs: uses: actions/setup-dotnet@main with: dotnet-version: 8.0.x - dotnet-quality: preview - name: Restore dependencies for ${{ matrix.project }} run: | diff --git a/.github/workflows/publish-to-gh-pages.yml b/.github/workflows/publish-to-gh-pages.yml index 6278ba3d..cc9eaa31 100644 --- a/.github/workflows/publish-to-gh-pages.yml +++ b/.github/workflows/publish-to-gh-pages.yml @@ -23,7 +23,6 @@ jobs: uses: actions/setup-dotnet@main with: dotnet-version: 8.0.x - dotnet-quality: preview - name: Publish .NET Core Project run: dotnet publish samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj -c Release -o release --nologo diff --git a/Directory.Build.props b/Directory.Build.props index f675dc22..ab82f05d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,46 +1,31 @@ - - <_ParentDirectoryBuildPropsPath Condition="'$(_DirectoryBuildPropsFile)' != ''">$([System.IO.Path]::Combine('..', '$(_DirectoryBuildPropsFile)')) - - - - - - 3 - preview - strict - - - - net7.0;net8.0 - - - - - 7.0.0 - 7.0.0 - 7.0.12 - 7.0.12 - - - - - 8.0.0-rc.2.23479.6 - 8.0.0-rc.2.23479.6 - 8.0.0-rc.2.23480.2 - 8.0.0-rc.2.23480.2 - - - - true - - - - true - - - - true - https://github.com/IEvangelist/azure-cosmos-dotnet-repository - + + <_ParentDirectoryBuildPropsPath Condition="'$(_DirectoryBuildPropsFile)' != ''">$([System.IO.Path]::Combine('..', '$(_DirectoryBuildPropsFile)')) + + + + + + 3 + preview + strict + + + + net7.0;net8.0 + preview + + + + true + + + + true + + + + true + https://github.com/IEvangelist/azure-cosmos-dotnet-repository + \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 00000000..59b5c9bf --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,41 @@ + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blazorators.sln b/blazorators.sln index 38f5c0d8..bf0a0492 100644 --- a/blazorators.sln +++ b/blazorators.sln @@ -12,6 +12,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig .gitignore = .gitignore Directory.Build.props = Directory.Build.props + Directory.Packages.props = Directory.Packages.props LICENSE = LICENSE logo-large.png = logo-large.png logo.png = logo.png diff --git a/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj b/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj index a836a8db..557a0956 100644 --- a/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj +++ b/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj @@ -1,25 +1,24 @@  - - net8.0 - enable - enable - preview - + + net8.0 + enable + enable + - - - - - + + + + + - - - - - - - - + + + + + + + + diff --git a/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj b/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj index a2478125..138ed72f 100644 --- a/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj +++ b/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj @@ -1,23 +1,22 @@ - + - - net8.0 - enable - enable - preview - + + net8.0 + enable + enable + - - - + + + - - - - - - - - + + + + + + + + diff --git a/samples/BlazorServer.ExampleConsumer/GlobalUsings.cs b/samples/BlazorServer.ExampleConsumer/GlobalUsings.cs index 79b2dbdb..bb06cc89 100644 --- a/samples/BlazorServer.ExampleConsumer/GlobalUsings.cs +++ b/samples/BlazorServer.ExampleConsumer/GlobalUsings.cs @@ -3,7 +3,7 @@ global using System.Text.Json; global using System.Text.Json.Serialization; -global using BlazorServer.ExampleConsumer.Models; -global using Microsoft.AspNetCore.Components; global using Microsoft.JSInterop; +global using Microsoft.AspNetCore.Components; +global using BlazorServer.ExampleConsumer.Models; global using static System.Globalization.CultureInfo; diff --git a/src/Blazor.Geolocation.WebAssembly/Blazor.Geolocation.WebAssembly.csproj b/src/Blazor.Geolocation.WebAssembly/Blazor.Geolocation.WebAssembly.csproj index fcb8a4cf..766ead73 100644 --- a/src/Blazor.Geolocation.WebAssembly/Blazor.Geolocation.WebAssembly.csproj +++ b/src/Blazor.Geolocation.WebAssembly/Blazor.Geolocation.WebAssembly.csproj @@ -1,79 +1,77 @@ - - $(DefaultTargetFrameworks) - enable - enable - preview - 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 - true - 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 fd433f2d..813f855d 100644 --- a/src/Blazor.Geolocation/Blazor.Geolocation.csproj +++ b/src/Blazor.Geolocation/Blazor.Geolocation.csproj @@ -1,83 +1,81 @@ - - $(DefaultTargetFrameworks) - enable - enable - preview - 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 - true - 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 25455400..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 - preview - 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 3b7b9b76..0a78434e 100644 --- a/src/Blazor.LocalStorage/Blazor.LocalStorage.csproj +++ b/src/Blazor.LocalStorage/Blazor.LocalStorage.csproj @@ -1,74 +1,74 @@ - - $(DefaultTargetFrameworks) - enable - enable - preview - 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 d5d4e66d..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 - preview - 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.Serialization/Blazor.Serialization.csproj b/src/Blazor.Serialization/Blazor.Serialization.csproj index 1ae5bdc6..37171417 100644 --- a/src/Blazor.Serialization/Blazor.Serialization.csproj +++ b/src/Blazor.Serialization/Blazor.Serialization.csproj @@ -1,68 +1,67 @@ - - $(DefaultTargetFrameworks) - enable - enable - preview - 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 750d097d..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 - preview - 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 642ff9a4..c88ab40d 100644 --- a/src/Blazor.SessionStorage/Blazor.SessionStorage.csproj +++ b/src/Blazor.SessionStorage/Blazor.SessionStorage.csproj @@ -1,74 +1,74 @@ - - $(DefaultTargetFrameworks) - enable - enable - preview - 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/Blazor.SourceGenerators.csproj b/src/Blazor.SourceGenerators/Blazor.SourceGenerators.csproj index e127678e..9b8e5def 100644 --- a/src/Blazor.SourceGenerators/Blazor.SourceGenerators.csproj +++ b/src/Blazor.SourceGenerators/Blazor.SourceGenerators.csproj @@ -1,18 +1,18 @@  - - netstandard2.0 - latest - enable - enable - preview - true - Copyright © David Pine. All rights reserved. Licensed under the MIT License. - true - - true - - false + + netstandard2.0 + preview + enable + enable + true + Copyright © David Pine. All rights reserved. Licensed under the MIT License. + true + + + true + + false en-US $([System.DateTime]::Now.ToString(yyyyMMdd)) @@ -43,7 +43,7 @@ embedded false false - NU5125;NU5039; + NU5125;NU5039 true https://github.com/IEvangelist/blazorators LICENSE @@ -57,51 +57,45 @@ 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/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.SpeechRecognition.WebAssembly/Blazor.SpeechRecognition.WebAssembly.csproj b/src/Blazor.SpeechRecognition.WebAssembly/Blazor.SpeechRecognition.WebAssembly.csproj index cbf09f2a..541b8b98 100644 --- a/src/Blazor.SpeechRecognition.WebAssembly/Blazor.SpeechRecognition.WebAssembly.csproj +++ b/src/Blazor.SpeechRecognition.WebAssembly/Blazor.SpeechRecognition.WebAssembly.csproj @@ -1,71 +1,71 @@ - - $(DefaultTargetFrameworks) - enable - enable - preview - 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/Blazor.SpeechRecognition.csproj b/src/Blazor.SpeechRecognition/Blazor.SpeechRecognition.csproj index d939b4ec..6cfef7f2 100644 --- a/src/Blazor.SpeechRecognition/Blazor.SpeechRecognition.csproj +++ b/src/Blazor.SpeechRecognition/Blazor.SpeechRecognition.csproj @@ -1,71 +1,71 @@ - - $(DefaultTargetFrameworks) - enable - enable - preview - 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.SpeechSynthesis.WebAssembly/Blazor.SpeechSynthesis.WebAssembly.csproj b/src/Blazor.SpeechSynthesis.WebAssembly/Blazor.SpeechSynthesis.WebAssembly.csproj index 2266d981..5474bb46 100644 --- a/src/Blazor.SpeechSynthesis.WebAssembly/Blazor.SpeechSynthesis.WebAssembly.csproj +++ b/src/Blazor.SpeechSynthesis.WebAssembly/Blazor.SpeechSynthesis.WebAssembly.csproj @@ -1,75 +1,75 @@ - - $(DefaultTargetFrameworks) - enable - enable - preview - 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 6aa82f3b..fe948d66 100644 --- a/src/Blazor.SpeechSynthesis/Blazor.SpeechSynthesis.csproj +++ b/src/Blazor.SpeechSynthesis/Blazor.SpeechSynthesis.csproj @@ -1,82 +1,75 @@ - - $(DefaultTargetFrameworks) - enable - enable - preview - 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 f531763c..5e984317 100644 --- a/tests/Blazor.ExampleConsumer.EndToEndTests/Blazor.ExampleConsumer.EndToEndTests.csproj +++ b/tests/Blazor.ExampleConsumer.EndToEndTests/Blazor.ExampleConsumer.EndToEndTests.csproj @@ -1,12 +1,11 @@  - - net8.0 - enable - preview - enable - false - + + net8.0 + enable + enable + false + @@ -18,18 +17,18 @@ - - - - - - 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 + all + + diff --git a/tests/Blazor.SourceGenerators.Tests/Blazor.SourceGenerators.Tests.csproj b/tests/Blazor.SourceGenerators.Tests/Blazor.SourceGenerators.Tests.csproj index 63aa2e64..15eda9c4 100644 --- a/tests/Blazor.SourceGenerators.Tests/Blazor.SourceGenerators.Tests.csproj +++ b/tests/Blazor.SourceGenerators.Tests/Blazor.SourceGenerators.Tests.csproj @@ -1,27 +1,26 @@  - - net8.0 - enable - enable - preview - false - + + net8.0 + enable + enable + false + - - - - - - - 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 + all + + From 3c8fc176edbdec61de8e425891abf2bc32ea6ac0 Mon Sep 17 00:00:00 2001 From: David Pine Date: Fri, 17 Nov 2023 13:18:49 -0600 Subject: [PATCH 07/41] Fix merge issues. --- .../GlobalUsings.cs | 10 +- .../Pages/Error.cshtml.cs | 7 +- .../Blazor.SourceGenerators.csproj | 91 +++++++++---------- 3 files changed, 53 insertions(+), 55 deletions(-) diff --git a/samples/BlazorServer.ExampleConsumer/GlobalUsings.cs b/samples/BlazorServer.ExampleConsumer/GlobalUsings.cs index bb06cc89..580cd479 100644 --- a/samples/BlazorServer.ExampleConsumer/GlobalUsings.cs +++ b/samples/BlazorServer.ExampleConsumer/GlobalUsings.cs @@ -1,9 +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.Models; + +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 5a4b77f6..8a74da4c 100644 --- a/samples/BlazorServer.ExampleConsumer/Pages/Error.cshtml.cs +++ b/samples/BlazorServer.ExampleConsumer/Pages/Error.cshtml.cs @@ -1,14 +1,11 @@ // 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(ILogger logger) : PageModel +public class ErrorModel : PageModel { public string? RequestId { get; set; } diff --git a/src/Blazor.SourceGenerators/Blazor.SourceGenerators.csproj b/src/Blazor.SourceGenerators/Blazor.SourceGenerators.csproj index 9b8e5def..eff104ec 100644 --- a/src/Blazor.SourceGenerators/Blazor.SourceGenerators.csproj +++ b/src/Blazor.SourceGenerators/Blazor.SourceGenerators.csproj @@ -14,58 +14,53 @@ 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 - - - - - From b41b4940b101288c61a4cc33bdf314be2b295f11 Mon Sep 17 00:00:00 2001 From: David Pine Date: Fri, 17 Nov 2023 15:13:57 -0600 Subject: [PATCH 08/41] Upgrade a few versions... --- Directory.Packages.props | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 59b5c9bf..54f53b5a 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -2,15 +2,14 @@ true - - + - + @@ -18,17 +17,15 @@ - - + - + - @@ -36,6 +33,6 @@ - + \ No newline at end of file From e3e29ed7994d8f7188632b3336a5c0b327835411 Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Tue, 2 Jul 2024 19:04:51 +0200 Subject: [PATCH 09/41] chore: remove globals.json --- global.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 global.json 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 From bdd211c6cb4fa5035bd97b3f77ba28bae18771ae Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Tue, 2 Jul 2024 19:10:37 +0200 Subject: [PATCH 10/41] fix: merge issues --- blazorators.sln | 1 - .../AnalyzerReleases.Unshipped.md | 3 +- .../Diagnostics/Descriptors.cs | 12 ++-- .../JavaScriptInteropGenerator.cs | 15 ++--- .../Readers/TypeDeclarationReader.cs | 62 +++++++++---------- .../LibDomParserTests.cs | 6 +- 6 files changed, 43 insertions(+), 56 deletions(-) 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/src/Blazor.SourceGenerators/AnalyzerReleases.Unshipped.md b/src/Blazor.SourceGenerators/AnalyzerReleases.Unshipped.md index 4696b9bc..f2ab206c 100644 --- a/src/Blazor.SourceGenerators/AnalyzerReleases.Unshipped.md +++ b/src/Blazor.SourceGenerators/AnalyzerReleases.Unshipped.md @@ -8,4 +8,5 @@ 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 \ No newline at end of file +BR0004 | Blazorators.JSAutoGenericInteropAttribute | Error | Descriptors +BR0005 | Blazor.SourceGenerators.JavaScriptInteropGenerator.TryExecute | Warning | Descriptors \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Diagnostics/Descriptors.cs b/src/Blazor.SourceGenerators/Diagnostics/Descriptors.cs index 504a6fc6..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", @@ -24,7 +24,7 @@ static class Descriptors internal static readonly DiagnosticDescriptor UnableToParseGeneratorOptionsDiagnostic = new( "BR0003", "The GeneratorOptions required for source generation are unresolvable", - "JSAutoGenericInteropAttribute must provide the fully qualified 'Descriptors' type name.", + "JSAutoGenericInteropAttribute must provide the fully qualified 'Descriptors' type name", "Blazorators.JSAutoGenericInteropAttribute", DiagnosticSeverity.Error, true); @@ -32,7 +32,7 @@ static class Descriptors 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/JavaScriptInteropGenerator.cs b/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs index 3853e2b7..d5f1071f 100644 --- a/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs +++ b/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs @@ -12,17 +12,10 @@ internal sealed partial class JavaScriptInteropGenerator : ISourceGenerator (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); @@ -110,7 +103,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) { @@ -134,7 +127,7 @@ static bool IsDiagnosticError(GeneratorOptions options, GeneratorExecutionContex if (options.SupportsGenerics && context.Compilation.ReferencedAssemblyNames.Any(ai => - ai.Name.Equals("Blazor.Serialization", StringComparison.OrdinalIgnoreCase)) is false) + !ai.Name.Equals("Blazor.Serialization", StringComparison.OrdinalIgnoreCase))) { context.ReportDiagnostic( Diagnostic.Create( @@ -146,4 +139,4 @@ static bool IsDiagnosticError(GeneratorOptions options, GeneratorExecutionContex return false; } -} +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs index 7b787fbe..541fa34f 100644 --- a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs +++ b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs @@ -5,28 +5,37 @@ namespace Blazor.SourceGenerators.Readers; internal sealed partial class TypeDeclarationReader { - readonly Lazy _typeDeclarationText; + private readonly Lazy _typeDeclarationText; + private IDictionary? _typeAliasMap; + private IDictionary? _typeDeclarationMap; - IDictionary? _typeDeclarationMap; - IDictionary? _typeAliasMap; + private TypeDeclarationReader() + { + _typeDeclarationText = new Lazy(valueFactory: () => GetEmbeddedResourceText()); + } - private IDictionary TypeDeclarationMap => - _typeDeclarationMap ??= ReadTypeDeclarationMap(_typeDeclarationText.Value); + /// + /// For testing purposes. + /// + internal bool IsInitialized => TypeDeclarationMap is { Count: > 0 }; + + internal string RawSourceText => _typeDeclarationText.Value; private IDictionary TypeAliasMap => _typeAliasMap ??= ReadTypeAliasMap(_typeDeclarationText.Value); - internal string RawSourceText => _typeDeclarationText.Value; + private IDictionary TypeDeclarationMap => + _typeDeclarationMap ??= ReadTypeDeclarationMap(_typeDeclarationText.Value); - private TypeDeclarationReader( - Uri? typeDeclarationSource = null) - { - _typeDeclarationSource = typeDeclarationSource; - _typeDeclarationText = new Lazy( - valueFactory: () => GetEmbeddedResourceText()); - } + public bool TryGetDeclaration( + string typeName, out string? declaration) => + TypeDeclarationMap.TryGetValue(typeName, out declaration); - ConcurrentDictionary ReadTypeDeclarationMap(string typeDeclarations) + public bool TryGetTypeAlias( + string typeAliasName, out string? typeAlias) => + TypeAliasMap.TryGetValue(typeAliasName, out typeAlias); + + private static ConcurrentDictionary ReadTypeAliasMap(string typeDeclarations) { ConcurrentDictionary map = new(); @@ -35,12 +44,12 @@ ConcurrentDictionary ReadTypeDeclarationMap(string typeDeclarati if (typeDeclarations is { Length: > 0 }) { var matchCollection = - InterfaceRegex.Matches(typeDeclarations).Cast().Select(m => m.Value); + TypeRegex.Matches(typeDeclarations).Cast().Select(m => m.Value); Parallel.ForEach( matchCollection, match => { - var typeName = InterfaceTypeNameRegex.GetMatchGroupValue(match, "TypeName"); + var typeName = TypeNameRegex.GetMatchGroupValue(match, "TypeName"); if (typeName is not null) { map[typeName] = match; @@ -56,7 +65,7 @@ ConcurrentDictionary ReadTypeDeclarationMap(string typeDeclarati return map; } - ConcurrentDictionary ReadTypeAliasMap(string typeDeclarations) + private static ConcurrentDictionary ReadTypeDeclarationMap(string typeDeclarations) { ConcurrentDictionary map = new(); @@ -65,12 +74,12 @@ ConcurrentDictionary ReadTypeAliasMap(string typeDeclarations) if (typeDeclarations is { Length: > 0 }) { var matchCollection = - TypeRegex.Matches(typeDeclarations).Cast().Select(m => m.Value); + InterfaceRegex.Matches(typeDeclarations).Cast().Select(m => m.Value); Parallel.ForEach( matchCollection, match => { - var typeName = TypeNameRegex.GetMatchGroupValue(match, "TypeName"); + var typeName = InterfaceTypeNameRegex.GetMatchGroupValue(match, "TypeName"); if (typeName is not null) { map[typeName] = match; @@ -85,17 +94,4 @@ ConcurrentDictionary ReadTypeAliasMap(string typeDeclarations) return map; } - - /// - /// 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/tests/Blazor.SourceGenerators.Tests/LibDomParserTests.cs b/tests/Blazor.SourceGenerators.Tests/LibDomParserTests.cs index 4bdf6c4a..41ad0b5e 100644 --- a/tests/Blazor.SourceGenerators.Tests/LibDomParserTests.cs +++ b/tests/Blazor.SourceGenerators.Tests/LibDomParserTests.cs @@ -114,7 +114,7 @@ public void ParseStaticObjectCorrectly() Assert.Single(dependencies); Assert.True(dependencies.ContainsKey("PositionOptions")); } - + [Fact] public void VerifyLocalStorageCanBeReadByDefault() { @@ -126,7 +126,7 @@ public void VerifyLocalStorageCanBeReadByDefault() // Assert var properties = parserResult.Value?.Properties; Assert.NotNull(properties); - Assert.Equal(2, properties?.Count ?? 0); + Assert.Equal(2, properties.Count); var methods = parserResult.Value?.Methods; Assert.NotNull(methods); @@ -137,4 +137,4 @@ public void VerifyLocalStorageCanBeReadByDefault() Assert.Contains(methods, m => m.RawName is "clear"); Assert.Contains(methods, m => m.RawName is "key"); } -} +} \ No newline at end of file From f11467cedd9e189116c16fe50e15844886630e5a Mon Sep 17 00:00:00 2001 From: "Koja sig. Dennis" Date: Fri, 12 Jul 2024 17:27:24 +0200 Subject: [PATCH 11/41] feat: start using Typescript AST --- .../TypeDeclarationParser.Interfaces.cs | 57 ++++++++++++------- .../Parsers/TypeDeclarationParser.cs | 10 ++-- .../TypeDeclarationReader.EmbeddedResource.cs | 15 ++++- .../Readers/TypeDeclarationReader.Factory.cs | 14 ++--- .../Readers/TypeDeclarationReader.cs | 29 +++++++++- 5 files changed, 85 insertions(+), 40 deletions(-) diff --git a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs index 99b24d0a..abb252d1 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs @@ -1,6 +1,8 @@ // 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 @@ -9,7 +11,7 @@ internal sealed partial class TypeDeclarationParser { CSharpObject? cSharpObject = null; - var lineTokens = typeScriptTypeDeclaration.Split(new[] { '\n' }); + var lineTokens = typeScriptTypeDeclaration.Split('\n'); foreach (var (index, segment) in lineTokens.Select((s, i) => (i, s))) { if (index == 0) @@ -97,7 +99,7 @@ internal sealed partial class TypeDeclarationParser // 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) && + _reader.TryGetDeclaration(mappedType, out string? typeScriptDefinitionText) && typeScriptDefinitionText is not null) { var obj = ToObject(typeScriptDefinitionText); @@ -114,11 +116,28 @@ internal sealed partial class TypeDeclarationParser return cSharpObject; } + internal CSharpTopLevelObject? ToTopLevelObject(InterfaceDeclaration typeScriptTypeDeclaration) + { + var topLevelObject = new CSharpTopLevelObject(typeScriptTypeDeclaration.Identifier); + + var methods = typeScriptTypeDeclaration.OfKind(TypeScriptSyntaxKind.MethodSignature); + foreach (var method in methods) + { + var methodName = method.Identifier; + var parameters = method.OfKind(TypeScriptSyntaxKind.Parameter); + var returnType = method.OfKind(TypeScriptSyntaxKind.TypeReference).FirstOrDefault(); + + + } + + return topLevelObject; + } + internal CSharpTopLevelObject? ToTopLevelObject(string typeScriptTypeDeclaration) { CSharpTopLevelObject? topLevelObject = null; - var lineTokens = typeScriptTypeDeclaration.Split(new[] { '\n' }); + var lineTokens = typeScriptTypeDeclaration.Split('\n'); foreach (var (index, segment) in lineTokens.Select((s, i) => (i, s))) { if (index == 0) @@ -207,7 +226,7 @@ internal sealed partial class TypeDeclarationParser // 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) && + _reader.TryGetDeclaration(mappedType, out string? typeScriptDefinitionText) && typeScriptDefinitionText is not null) { var obj = ToObject(typeScriptDefinitionText); @@ -228,22 +247,16 @@ private string TryGetPrimitiveType(string type) { if (!TypeMap.PrimitiveTypes.IsPrimitiveType(type) && _reader.TryGetTypeAlias(type, out var typeAliasLine) && - typeAliasLine is not null) + typeAliasLine is not null && typeAliasLine.Replace(";", "").Split('=') + is { Length: 2 } split && split[1].Trim().Split('|') + is { Length: > 0 } values && + values.Select(v => v.Trim()).ToList() + is { Count: > 0 } list) { - if (typeAliasLine.Replace(";", "").Split('=') - is { Length: 2 } split) + var isStringAlias = list.TrueForAll(v => v.StartsWith("\"") && v.EndsWith("\"")); + if (isStringAlias) { - if (split[1].Trim().Split('|') - is { Length: > 0 } values && - values.Select(v => v.Trim())?.ToList() - is { Count: > 0 } list) - { - var isStringAlias = list.All(v => v.StartsWith("\"") && v.EndsWith("\"")); - if (isStringAlias) - { - type = "string"; - } - } + type = "string"; } } @@ -265,7 +278,7 @@ private CSharpMethod ToMethod( var nonArrayMethodReturnType = methodReturnType.Replace("[]", ""); if (!TypeMap.PrimitiveTypes.IsPrimitiveType(nonArrayMethodReturnType) && - _reader.TryGetDeclaration(nonArrayMethodReturnType, out var typeScriptDefinitionText) && + _reader.TryGetDeclaration(nonArrayMethodReturnType, out string? typeScriptDefinitionText) && typeScriptDefinitionText is not null) { var dependentType = ToObject(typeScriptDefinitionText); @@ -282,7 +295,7 @@ private CSharpMethod ToMethod( { CSharpAction? cSharpAction = null; - var lineTokens = typeScriptTypeDeclaration.Split(new[] { '\n' }); + var lineTokens = typeScriptTypeDeclaration.Split('\n'); foreach (var (index, segment) in lineTokens.Select((s, i) => (i, s))) { if (index == 0) @@ -362,7 +375,7 @@ internal static string CleanseReturnType(string returnType) => // Example input: // "(someCallback: CallbackType, someId?: number | null)" var trimmedParameters = parametersString.Replace("(", "").Replace(")", ""); - var parameterLineTokenizer = trimmedParameters.Split(new[] { ':', ',', }); + var parameterLineTokenizer = trimmedParameters.Split(':', ','); JavaScriptMethod? javaScriptMethod = new(rawName); foreach (var parameterPair in parameterLineTokenizer.Where(t => t.Length > 0).Chunk(2)) @@ -378,7 +391,7 @@ internal static string CleanseReturnType(string returnType) => // 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) && + _reader.TryGetDeclaration(parameterType, out string? typeScriptDefinitionText) && typeScriptDefinitionText is not null) { javaScriptMethod = javaScriptMethod with diff --git a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs index 2921982f..cf85e37e 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs @@ -1,13 +1,14 @@ // 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 { static readonly Lazy s_defaultParser = - new( - valueFactory: () => new TypeDeclarationParser(TypeDeclarationReader.Default)); + new(valueFactory: () => new TypeDeclarationParser(TypeDeclarationReader.Default)); readonly TypeDeclarationReader _reader; @@ -19,15 +20,14 @@ public ParserResult ParseTargetType(string typeName) { ParserResult result = new(ParserResultStatus.Unknown); - if (_reader.TryGetDeclaration(typeName, out var typeScriptDefinitionText) && - typeScriptDefinitionText is not null) + if (_reader.TryGetDeclaration(typeName, out InterfaceDeclaration? typescriptInterfaceDeclaration) && typescriptInterfaceDeclaration is not null) { try { result = result with { Status = ParserResultStatus.SuccessfullyParsed, - Value = ToTopLevelObject(typeScriptDefinitionText) + Value = ToTopLevelObject(typescriptInterfaceDeclaration) }; } 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 305301f1..e4bffb47 100644 --- a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.Factory.cs +++ b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.Factory.cs @@ -15,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.cs b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs index 541fa34f..0ba4319a 100644 --- a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs +++ b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs @@ -1,6 +1,9 @@ // 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 @@ -8,10 +11,19 @@ internal sealed partial class TypeDeclarationReader private readonly Lazy _typeDeclarationText; private IDictionary? _typeAliasMap; private IDictionary? _typeDeclarationMap; + private ITypeScriptAbstractSyntaxTree? _typeDeclarationTree; + + private TypeDeclarationReader(Uri typeDeclarationUri) + { + _typeDeclarationText = new Lazy(valueFactory: () => typeDeclarationUri switch + { + { IsAbsoluteUri: true } => GetRemoteResourceText(typeDeclarationUri.AbsoluteUri), + _ => GetEmbeddedResourceText() + }); + } - private TypeDeclarationReader() + private TypeDeclarationReader() : this(null!) { - _typeDeclarationText = new Lazy(valueFactory: () => GetEmbeddedResourceText()); } /// @@ -25,12 +37,23 @@ private TypeDeclarationReader() _typeAliasMap ??= ReadTypeAliasMap(_typeDeclarationText.Value); private IDictionary TypeDeclarationMap => - _typeDeclarationMap ??= ReadTypeDeclarationMap(_typeDeclarationText.Value); + _typeDeclarationMap ??= ReadTypeDeclarationMap(_typeDeclarationText.Value); + + private ITypeScriptAbstractSyntaxTree TypeDeclarationTree => + _typeDeclarationTree ??= TypeScriptAbstractSyntaxTree.FromSourceText(_typeDeclarationText.Value); public bool TryGetDeclaration( string typeName, out string? declaration) => TypeDeclarationMap.TryGetValue(typeName, out declaration); + public bool TryGetDeclaration(string typeName, out InterfaceDeclaration? declaration) + { + declaration = TypeDeclarationTree.RootNode.OfKind(TypeScriptSyntaxKind.InterfaceDeclaration) + .FirstOrDefault(node => node.Identifier == typeName) as InterfaceDeclaration; + + return declaration != null; + } + public bool TryGetTypeAlias( string typeAliasName, out string? typeAlias) => TypeAliasMap.TryGetValue(typeAliasName, out typeAlias); From c950ac04d12f620d3139d5ef9cb2ac52ad680ca4 Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Mon, 15 Jul 2024 10:36:20 +0200 Subject: [PATCH 12/41] chore: remove regex parsing for source, use new AST. For now use old method to parse objects. Use raw strings for tests --- .../TypeDeclarationParser.Interfaces.cs | 46 +++---- .../Parsers/TypeDeclarationParser.cs | 2 +- .../Readers/TypeDeclarationReader.cs | 90 +++---------- .../LibDomParserInterfacesTests.cs | 124 +++++++++--------- 4 files changed, 106 insertions(+), 156 deletions(-) diff --git a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs index abb252d1..9d2b4fcb 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs @@ -1,8 +1,6 @@ // 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 @@ -11,6 +9,8 @@ internal sealed partial class TypeDeclarationParser { CSharpObject? cSharpObject = null; + typeScriptTypeDeclaration = FormatInterfaceDeclaration(typeScriptTypeDeclaration); + var lineTokens = typeScriptTypeDeclaration.Split('\n'); foreach (var (index, segment) in lineTokens.Select((s, i) => (i, s))) { @@ -99,7 +99,7 @@ internal sealed partial class TypeDeclarationParser // 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 string? typeScriptDefinitionText) && + _reader.TryGetDeclaration(mappedType, out var typeScriptDefinitionText) && typeScriptDefinitionText is not null) { var obj = ToObject(typeScriptDefinitionText); @@ -116,27 +116,12 @@ internal sealed partial class TypeDeclarationParser return cSharpObject; } - internal CSharpTopLevelObject? ToTopLevelObject(InterfaceDeclaration typeScriptTypeDeclaration) - { - var topLevelObject = new CSharpTopLevelObject(typeScriptTypeDeclaration.Identifier); - - var methods = typeScriptTypeDeclaration.OfKind(TypeScriptSyntaxKind.MethodSignature); - foreach (var method in methods) - { - var methodName = method.Identifier; - var parameters = method.OfKind(TypeScriptSyntaxKind.Parameter); - var returnType = method.OfKind(TypeScriptSyntaxKind.TypeReference).FirstOrDefault(); - - - } - - return topLevelObject; - } - internal CSharpTopLevelObject? ToTopLevelObject(string typeScriptTypeDeclaration) { CSharpTopLevelObject? topLevelObject = null; + typeScriptTypeDeclaration = FormatInterfaceDeclaration(typeScriptTypeDeclaration); + var lineTokens = typeScriptTypeDeclaration.Split('\n'); foreach (var (index, segment) in lineTokens.Select((s, i) => (i, s))) { @@ -226,7 +211,7 @@ internal sealed partial class TypeDeclarationParser // 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 string? typeScriptDefinitionText) && + _reader.TryGetDeclaration(mappedType, out var typeScriptDefinitionText) && typeScriptDefinitionText is not null) { var obj = ToObject(typeScriptDefinitionText); @@ -243,6 +228,19 @@ internal sealed partial class TypeDeclarationParser return topLevelObject; } + + + private static string FormatInterfaceDeclaration(string typescriptInterfaceDeclaration) + { + var typescriptInterfaceDeclarationStart = InterfaceRegex.Match(typescriptInterfaceDeclaration); + if (typescriptInterfaceDeclarationStart.Success && typescriptInterfaceDeclarationStart.Index >= 0) + { + typescriptInterfaceDeclaration = typescriptInterfaceDeclaration.Substring(typescriptInterfaceDeclarationStart.Index); + } + + return typescriptInterfaceDeclaration; + } + private string TryGetPrimitiveType(string type) { if (!TypeMap.PrimitiveTypes.IsPrimitiveType(type) && @@ -278,7 +276,7 @@ private CSharpMethod ToMethod( var nonArrayMethodReturnType = methodReturnType.Replace("[]", ""); if (!TypeMap.PrimitiveTypes.IsPrimitiveType(nonArrayMethodReturnType) && - _reader.TryGetDeclaration(nonArrayMethodReturnType, out string? typeScriptDefinitionText) && + _reader.TryGetDeclaration(nonArrayMethodReturnType, out var typeScriptDefinitionText) && typeScriptDefinitionText is not null) { var dependentType = ToObject(typeScriptDefinitionText); @@ -295,6 +293,8 @@ private CSharpMethod ToMethod( { CSharpAction? cSharpAction = null; + typeScriptTypeDeclaration = FormatInterfaceDeclaration(typeScriptTypeDeclaration); + var lineTokens = typeScriptTypeDeclaration.Split('\n'); foreach (var (index, segment) in lineTokens.Select((s, i) => (i, s))) { @@ -391,7 +391,7 @@ internal static string CleanseReturnType(string returnType) => // 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 string? typeScriptDefinitionText) && + _reader.TryGetDeclaration(parameterType, out var typeScriptDefinitionText) && typeScriptDefinitionText is not null) { javaScriptMethod = javaScriptMethod with diff --git a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs index cf85e37e..0ebfd1ec 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs @@ -20,7 +20,7 @@ public ParserResult ParseTargetType(string typeName) { ParserResult result = new(ParserResultStatus.Unknown); - if (_reader.TryGetDeclaration(typeName, out InterfaceDeclaration? typescriptInterfaceDeclaration) && typescriptInterfaceDeclaration is not null) + if (_reader.TryGetDeclaration(typeName, out var typescriptInterfaceDeclaration) && typescriptInterfaceDeclaration is not null) { try { diff --git a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs index 0ba4319a..65485e2a 100644 --- a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs +++ b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs @@ -9,8 +9,6 @@ namespace Blazor.SourceGenerators.Readers; internal sealed partial class TypeDeclarationReader { private readonly Lazy _typeDeclarationText; - private IDictionary? _typeAliasMap; - private IDictionary? _typeDeclarationMap; private ITypeScriptAbstractSyntaxTree? _typeDeclarationTree; private TypeDeclarationReader(Uri typeDeclarationUri) @@ -29,92 +27,38 @@ private TypeDeclarationReader() : this(null!) /// /// For testing purposes. /// - internal bool IsInitialized => TypeDeclarationMap is { Count: > 0 }; + internal bool IsInitialized => TypeDeclarationTree.RootNode is { Count: > 0 }; internal string RawSourceText => _typeDeclarationText.Value; - private IDictionary TypeAliasMap => - _typeAliasMap ??= ReadTypeAliasMap(_typeDeclarationText.Value); - - private IDictionary TypeDeclarationMap => - _typeDeclarationMap ??= ReadTypeDeclarationMap(_typeDeclarationText.Value); - private ITypeScriptAbstractSyntaxTree TypeDeclarationTree => _typeDeclarationTree ??= TypeScriptAbstractSyntaxTree.FromSourceText(_typeDeclarationText.Value); - public bool TryGetDeclaration( - string typeName, out string? declaration) => - TypeDeclarationMap.TryGetValue(typeName, out declaration); - - public bool TryGetDeclaration(string typeName, out InterfaceDeclaration? declaration) + public bool TryGetDeclaration(string typeName, out string? declaration) { - declaration = TypeDeclarationTree.RootNode.OfKind(TypeScriptSyntaxKind.InterfaceDeclaration) + var node = TypeDeclarationTree.RootNode.OfKind(TypeScriptSyntaxKind.InterfaceDeclaration) .FirstOrDefault(node => node.Identifier == typeName) as InterfaceDeclaration; - return declaration != null; - } - - public bool TryGetTypeAlias( - string typeAliasName, out string? typeAlias) => - TypeAliasMap.TryGetValue(typeAliasName, out typeAlias); + declaration = node == null + ? string.Empty + : node.GetText().ToString(); - private static ConcurrentDictionary ReadTypeAliasMap(string typeDeclarations) - { - ConcurrentDictionary map = new(); + declaration = declaration.TrimStart('\r', '\n'); - 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}"); - } - - return map; + return declaration != null; } - private static ConcurrentDictionary ReadTypeDeclarationMap(string typeDeclarations) + public bool TryGetTypeAlias(string typeName, out string? declaration) { - ConcurrentDictionary map = new(); + var node = TypeDeclarationTree.RootNode.OfKind(TypeScriptSyntaxKind.TypeAliasDeclaration) + .FirstOrDefault(node => node.Identifier == typeName) as TypeAliasDeclaration; - 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}"); - } + declaration = node == null + ? string.Empty + : node.GetText().ToString(); + + declaration = declaration.TrimStart('\r', '\n'); - return map; + return declaration != null; } } \ No newline at end of file diff --git a/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs b/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs index 83ac58d2..fc66b898 100644 --- a/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs +++ b/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs @@ -11,64 +11,68 @@ 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 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 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 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!; + } + + """; Assert.NotNull(actual); @@ -83,11 +87,13 @@ 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 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); From 7007138584f089188792ba41e55f7138127e9745 Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Mon, 15 Jul 2024 11:35:39 +0200 Subject: [PATCH 13/41] chore: deep cleanup, apply sonar lint performance suggestions. Tests work as expected --- .../Builders/Indentation.cs | 2 +- .../Builders/IndentationAdjustment.cs | 2 +- .../Builders/NodeToSourceBuilder.cs | 1 - .../Parsers/TypeDeclarationParser.cs | 2 - .../Readers/TypeDeclarationReader.cs | 8 +- .../TypeScript/Compiler/Core.cs | 26 +- .../TypeScript/Compiler/Diagnostics.cs | 92 +++--- .../TypeScript/Compiler/Factory.cs | 4 +- .../TypeScript/Compiler/JsDocParser.cs | 74 ++--- .../TypeScript/Compiler/Parser.cs | 266 ++++++++++-------- .../TypeScript/Compiler/Scanner.cs | 180 ++++++------ .../TypeScript/Compiler/Ts.cs | 22 +- .../TypeScript/Compiler/Utilities.cs | 2 +- .../TypeScript/Types/EmitResult.cs | 5 +- .../TypeScript/Types/Node.cs | 3 +- .../TypeScript/Types/ParsedCommandLine.cs | 5 +- .../TypeScript/Types/SourceFile.cs | 5 +- .../TypeScript/Types/TransformationResult.cs | 5 +- .../Types/DependentTypeMapper.cs | 6 +- 19 files changed, 393 insertions(+), 317 deletions(-) diff --git a/src/Blazor.SourceGenerators/Builders/Indentation.cs b/src/Blazor.SourceGenerators/Builders/Indentation.cs index 86e294f9..25765bb0 100644 --- a/src/Blazor.SourceGenerators/Builders/Indentation.cs +++ b/src/Blazor.SourceGenerators/Builders/Indentation.cs @@ -27,7 +27,7 @@ internal readonly record struct Indentation(int Level, int Spaces = 4) /// 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. /// diff --git a/src/Blazor.SourceGenerators/Builders/IndentationAdjustment.cs b/src/Blazor.SourceGenerators/Builders/IndentationAdjustment.cs index 987a031d..5efb9ef9 100644 --- a/src/Blazor.SourceGenerators/Builders/IndentationAdjustment.cs +++ b/src/Blazor.SourceGenerators/Builders/IndentationAdjustment.cs @@ -17,7 +17,7 @@ internal enum IndentationAdjustment /// The indentation level is increased. /// Increase, - + /// /// The indentation level is decreased. /// diff --git a/src/Blazor.SourceGenerators/Builders/NodeToSourceBuilder.cs b/src/Blazor.SourceGenerators/Builders/NodeToSourceBuilder.cs index b8dce1bd..b2815d70 100644 --- a/src/Blazor.SourceGenerators/Builders/NodeToSourceBuilder.cs +++ b/src/Blazor.SourceGenerators/Builders/NodeToSourceBuilder.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; namespace Blazor.SourceGenerators.Builders; diff --git a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs index 0ebfd1ec..3710a37f 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs @@ -1,8 +1,6 @@ // 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 diff --git a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs index 65485e2a..59b4f022 100644 --- a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs +++ b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs @@ -36,8 +36,8 @@ private TypeDeclarationReader() : this(null!) public bool TryGetDeclaration(string typeName, out string? declaration) { - var node = TypeDeclarationTree.RootNode.OfKind(TypeScriptSyntaxKind.InterfaceDeclaration) - .FirstOrDefault(node => node.Identifier == typeName) as InterfaceDeclaration; + var node = TypeDeclarationTree.RootNode.OfKind(TypeScriptSyntaxKind.InterfaceDeclaration) + .FirstOrDefault(node => node.Identifier == typeName) as InterfaceDeclaration; declaration = node == null ? string.Empty @@ -50,8 +50,8 @@ public bool TryGetDeclaration(string typeName, out string? declaration) public bool TryGetTypeAlias(string typeName, out string? declaration) { - var node = TypeDeclarationTree.RootNode.OfKind(TypeScriptSyntaxKind.TypeAliasDeclaration) - .FirstOrDefault(node => node.Identifier == typeName) as TypeAliasDeclaration; + var node = TypeDeclarationTree.RootNode.OfKind(TypeScriptSyntaxKind.TypeAliasDeclaration) + .FirstOrDefault(node => node.Identifier == typeName) as TypeAliasDeclaration; declaration = node == null ? string.Empty 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/Node.cs b/src/Blazor.SourceGenerators/TypeScript/Types/Node.cs index 8c364fe4..73e0e3c9 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,7 +9,7 @@ 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; 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 From 24cc118776ee821740ba8ed24cd8218d5291bd65 Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Mon, 15 Jul 2024 12:15:34 +0200 Subject: [PATCH 14/41] fix: source generator not finding reference assembly --- src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs b/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs index d5f1071f..6e16a53f 100644 --- a/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs +++ b/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs @@ -126,8 +126,8 @@ private static bool IsDiagnosticError(GeneratorOptions options, GeneratorExecuti } if (options.SupportsGenerics && - context.Compilation.ReferencedAssemblyNames.Any(ai => - !ai.Name.Equals("Blazor.Serialization", StringComparison.OrdinalIgnoreCase))) + !context.Compilation.ReferencedAssemblyNames.Any(ai => + ai.Name.Equals("Blazor.Serialization", StringComparison.OrdinalIgnoreCase))) { context.ReportDiagnostic( Diagnostic.Create( From f5438d0fa3713c3974b0595b187012985efd7fe4 Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Tue, 16 Jul 2024 08:23:51 +0200 Subject: [PATCH 15/41] chore: code formatting --- .../JavaScriptInteropGenerator.cs | 30 ++++++++----------- .../TypeScript/Types/Node.cs | 7 ++--- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs b/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs index 6e16a53f..7ba96a72 100644 --- a/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs +++ b/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs @@ -17,8 +17,7 @@ internal sealed partial class JavaScriptInteropGenerator : ISourceGenerator public void Initialize(GeneratorInitializationContext context) { // 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); @@ -30,8 +29,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) @@ -64,17 +62,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) @@ -126,8 +122,8 @@ private static bool IsDiagnosticError(GeneratorOptions options, GeneratorExecuti } if (options.SupportsGenerics && - !context.Compilation.ReferencedAssemblyNames.Any(ai => - ai.Name.Equals("Blazor.Serialization", StringComparison.OrdinalIgnoreCase))) + !context.Compilation.ReferencedAssemblyNames.Any( + ai => ai.Name.Equals("Blazor.Serialization", StringComparison.OrdinalIgnoreCase))) { context.ReportDiagnostic( Diagnostic.Create( diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/Node.cs b/src/Blazor.SourceGenerators/TypeScript/Types/Node.cs index 73e0e3c9..54f9db9a 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/Node.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/Node.cs @@ -17,11 +17,8 @@ public class Node : TextRange, INode 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(); + ? ((Identifier)this).Text + : ((Identifier)Children.Find(child => child.Kind is TypeScriptSyntaxKind.Identifier)).Text; public int ParentId { get; set; } public int Depth { get; set; } From 090bd49d97c0dc7460904b2b100d5d2da4ec009d Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Tue, 16 Jul 2024 13:52:44 +0200 Subject: [PATCH 16/41] feat: implmented AST into the parser and source writer --- .../CSharp/CSharpAction.cs | 2 +- .../CSharp/CSharpMethod.cs | 5 +- .../CSharp/CSharpTopLevelObject.cs | 16 +- .../Extensions/CSharpMethodExtensions.cs | 70 +-- .../GeneratorExecutionContextExtensions.cs | 21 +- .../Extensions/ListExtensions.cs | 2 +- .../TypeDeclarationParser.Interfaces.cs | 555 +++++------------- .../Parsers/TypeDeclarationParser.cs | 4 +- .../Readers/TypeDeclarationReader.cs | 30 +- .../TypeScript/Types/Node.cs | 12 +- 10 files changed, 226 insertions(+), 491 deletions(-) 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/CSharpTopLevelObject.cs b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs index 2cad3a0b..cea0b925 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs @@ -8,9 +8,9 @@ namespace Blazor.SourceGenerators.CSharp; internal sealed partial record CSharpTopLevelObject(string RawTypeName) : ICSharpDependencyGraphObject { - public List? Properties { get; init; } = []; + public List Properties { get; init; } = []; - public List? Methods { get; init; } = []; + public List Methods { get; init; } = []; public Dictionary DependentTypes { get; init; } = new(StringComparer.OrdinalIgnoreCase); @@ -37,9 +37,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() @@ -108,7 +106,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 +328,7 @@ internal string ToImplementationString( } } } - else if (options.OnlyGeneratePureJS is false) + else if (!options.OnlyGeneratePureJS) { var genericTypeArgs = details.GenericTypeArgs ?? MethodBuilderDetails.ToGenericTypeArgument( @@ -496,9 +494,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/Extensions/CSharpMethodExtensions.cs b/src/Blazor.SourceGenerators/Extensions/CSharpMethodExtensions.cs index e0113b9a..58385796 100644 --- a/src/Blazor.SourceGenerators/Extensions/CSharpMethodExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/CSharpMethodExtensions.cs @@ -10,45 +10,39 @@ 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) @@ -70,21 +64,19 @@ 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 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 returnType = $"ValueTask<{method.RawReturnTypeName}>"; return (returnType, primitiveType); } diff --git a/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs b/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs index b289efcd..9629d7cb 100644 --- a/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs @@ -5,13 +5,10 @@ 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 +28,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 +44,7 @@ internal static GeneratorExecutionContext AddImplementationSource( context.AddSource( $"{implementation}".ToGeneratedFileName(), SourceText.From( - topLevelObject.ToImplementationString( - options, - @namespace), + topLevelObject.ToImplementationString(options, @namespace), Encoding.UTF8)); return context; @@ -66,9 +59,7 @@ internal static GeneratorExecutionContext AddDependencyInjectionExtensionsSource 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/Parsers/TypeDeclarationParser.Interfaces.cs b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs index 9d2b4fcb..714beb33 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs @@ -1,496 +1,259 @@ // 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; - - typeScriptTypeDeclaration = FormatInterfaceDeclaration(typeScriptTypeDeclaration); - - var lineTokens = typeScriptTypeDeclaration.Split('\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; - } - - var line = segment.Trim(); - if (line.Length == 0) - { - continue; - } - - if (line == "}") - { - // We're done - break; - } - - 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"); - - if (methodName is null || parameters is null || returnType is null) - { - continue; - } - - var (parameterDefinitions, javaScriptMethod) = - ParseParameters( - cSharpObject.TypeName, - methodName, - parameters, - obj => cSharpObject.DependentTypes![obj.TypeName] = obj); - - - var cSharpMethod = - ToMethod(methodName, returnType, parameterDefinitions, javaScriptMethod); - cSharpObject.Methods[cSharpMethod.RawName] = cSharpMethod; - - continue; - } - - if (IsProperty(line, out var property) && property is not null) - { - var name = property.GetGroupValue("Name"); - var type = property.GetGroupValue("Type"); - - if (name is null || type is null) - { - 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); - cSharpObject.Properties[cSharpProperty.RawName] = cSharpProperty; - - var mappedType = cSharpProperty.MappedTypeName; - - // 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; - } - } - - continue; - } + return ToObject(typescriptInterface); } - return cSharpObject; + return default!; } - internal CSharpTopLevelObject? ToTopLevelObject(string typeScriptTypeDeclaration) + internal CSharpObject ToObject(InterfaceDeclaration typescriptInterface) { - CSharpTopLevelObject? topLevelObject = null; + var heritage = typescriptInterface.HeritageClauses? + .Where(heritage => heritage.Identifier is not " extends EventTarget") + .Select(heritage => heritage.Identifier) + .ToArray(); - typeScriptTypeDeclaration = FormatInterfaceDeclaration(typeScriptTypeDeclaration); + var subclass = heritage is null || heritage.Length == 0 ? "" : string.Join(", ", heritage); - var lineTokens = typeScriptTypeDeclaration.Split('\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) - { - topLevelObject = new(typeName); - continue; - } - else - { - break; - } - } - - if (topLevelObject is null) - { - break; - } - - var line = segment.Trim(); - if (line.Length == 0) - { - continue; - } - - if (line == "}") - { - // We're done - break; - } - - 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"); - - if (methodName is null || parameters is null || returnType is null) - { - continue; - } - - var (parameterDefinitions, javaScriptMethod) = - ParseParameters( - topLevelObject.RawTypeName, - methodName, - parameters, - obj => topLevelObject.DependentTypes![obj.TypeName] = obj); - - var cSharpMethod = - ToMethod(methodName, returnType, parameterDefinitions, javaScriptMethod); - - topLevelObject.Methods!.Add(cSharpMethod); - - continue; - } - - if (IsProperty(line, out var property) && property is not null) - { - var name = property.GetGroupValue("Name"); - var type = property.GetGroupValue("Type"); - - if (name is null || type is null) - { - continue; - } + var csharpObject = new CSharpObject(typescriptInterface.Identifier, subclass); - var isReadonly = name.StartsWith("readonly "); - var isNullable = name.EndsWith("?") || type.Contains("| null"); + var objectMethods = typescriptInterface.OfKind(TypeScriptSyntaxKind.MethodSignature); + var methods = ParseMethods( + csharpObject.TypeName, + objectMethods, + (dependency) => csharpObject.DependentTypes[dependency.TypeName] = dependency); - name = name.Replace("?", "").Replace("readonly ", ""); - type = TryGetPrimitiveType(type); - - CSharpProperty cSharpProperty = - new(name, - type, - isNullable, - isReadonly); - - topLevelObject.Properties!.Add(cSharpProperty); + csharpObject = csharpObject with + { + Methods = methods + .GroupBy(method => method.RawName) + .ToDictionary(method => method.Key, method => method.Last()) + }; - var mappedType = cSharpProperty.MappedTypeName; + var objectProperties = typescriptInterface.OfKind(TypeScriptSyntaxKind.PropertySignature); + var properties = ParseProperties( + objectProperties, + (dependency) => csharpObject.DependentTypes[dependency.TypeName] = dependency); - // 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) - { - topLevelObject.DependentTypes![obj.TypeName] = obj; - } - } - - continue; - } - } + csharpObject = csharpObject with + { + Properties = properties + .GroupBy(property => property.RawName) + .ToDictionary(property => property.Key, method => method.Last()) + }; - return topLevelObject; + return csharpObject; } - - - private static string FormatInterfaceDeclaration(string typescriptInterfaceDeclaration) + internal CSharpTopLevelObject ToTopLevelObject(string typeName) { - var typescriptInterfaceDeclarationStart = InterfaceRegex.Match(typescriptInterfaceDeclaration); - if (typescriptInterfaceDeclarationStart.Success && typescriptInterfaceDeclarationStart.Index >= 0) + if (TryGetCustomType(typeName, out var typescriptInterface)) { - typescriptInterfaceDeclaration = typescriptInterfaceDeclaration.Substring(typescriptInterfaceDeclarationStart.Index); + return ToTopLevelObject(typescriptInterface); } - return typescriptInterfaceDeclaration; + return default!; } - private string TryGetPrimitiveType(string type) + internal CSharpTopLevelObject ToTopLevelObject(InterfaceDeclaration typescriptInterface) { - if (!TypeMap.PrimitiveTypes.IsPrimitiveType(type) && - _reader.TryGetTypeAlias(type, out var typeAliasLine) && - typeAliasLine is not null && typeAliasLine.Replace(";", "").Split('=') - is { Length: 2 } split && split[1].Trim().Split('|') - is { Length: > 0 } values && - values.Select(v => v.Trim()).ToList() - is { Count: > 0 } list) - { - var isStringAlias = list.TrueForAll(v => v.StartsWith("\"") && v.EndsWith("\"")); - if (isStringAlias) - { - type = "string"; - } - } + var csharpTopLevelObject = new CSharpTopLevelObject(typescriptInterface.Identifier); - return type; - } + var objectMethods = typescriptInterface.OfKind(TypeScriptSyntaxKind.MethodSignature); + var methods = ParseMethods( + csharpTopLevelObject.RawTypeName, + objectMethods, + (dependency) => csharpTopLevelObject.DependentTypes[dependency.TypeName] = dependency); - private CSharpMethod ToMethod( - string methodName, - string returnType, - List parameterDefinitions, - JavaScriptMethod? javaScriptMethod) - { - var methodReturnType = CleanseReturnType(returnType); - CSharpMethod cSharpMethod = - new(methodName, - methodReturnType, - parameterDefinitions, - javaScriptMethod); + csharpTopLevelObject.Methods.AddRange(methods); - var nonArrayMethodReturnType = methodReturnType.Replace("[]", ""); - if (!TypeMap.PrimitiveTypes.IsPrimitiveType(nonArrayMethodReturnType) && - _reader.TryGetDeclaration(nonArrayMethodReturnType, out var typeScriptDefinitionText) && - typeScriptDefinitionText is not null) - { - var dependentType = ToObject(typeScriptDefinitionText); - if (dependentType is not null) - { - cSharpMethod.DependentTypes![nonArrayMethodReturnType] = dependentType; - } - } + var objectProperties = typescriptInterface.OfKind(TypeScriptSyntaxKind.PropertySignature); + var properties = ParseProperties( + objectProperties, + (dependency) => csharpTopLevelObject.DependentTypes[dependency.TypeName] = dependency); + + csharpTopLevelObject.Properties.AddRange(properties); - return cSharpMethod; + return csharpTopLevelObject; } - internal CSharpAction? ToAction(string typeScriptTypeDeclaration) + private IEnumerable ParseMethods(string rawTypeName, IEnumerable objectMethods, Action appendDependency) { - CSharpAction? cSharpAction = null; - - typeScriptTypeDeclaration = FormatInterfaceDeclaration(typeScriptTypeDeclaration); - - var lineTokens = typeScriptTypeDeclaration.Split('\n'); - foreach (var (index, segment) in lineTokens.Select((s, i) => (i, s))) + ICollection methods = []; + foreach (var method in objectMethods.Cast()) { - if (index == 0) - { - var typeName = InterfaceTypeNameRegex.GetMatchGroupValue(segment, "TypeName"); - if (typeName is not null) - { - cSharpAction = new(typeName); - continue; - } - else - { - break; - } - } + var methodName = method.Identifier; + var methodParameters = method.Parameters; + var methodReturnType = method.Type.GetText().ToString().Trim(); - if (cSharpAction 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; - } - - if (IsAction(line, out var action) && action is not null) - { - var parameters = action.GetGroupValue("Parameters"); - var returnType = action.GetGroupValue("ReturnType"); - - if (parameters is null || returnType is null) - { - continue; - } + var (csharpParameters, javascriptMethod) = ParseParameters( + rawTypeName, + methodName, + methodParameters, + appendDependency); - var (parameterDefinitions, _) = - ParseParameters( - cSharpAction.RawName, - cSharpAction.RawName, - parameters, - obj => cSharpAction.DependentTypes![obj.TypeName] = obj); + var csharpMethod = ToMethod(methodName, methodReturnType, csharpParameters, javascriptMethod); - cSharpAction = cSharpAction with - { - ParameterDefinitions = parameterDefinitions - }; - - continue; - } + methods.Add(csharpMethod); } - return cSharpAction; + return methods; } - 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 (IList, JavaScriptMethod) ParseParameters(string rawTypeName, string methodName, NodeArray methodParameters, Action appendDependency) { - List parameters = []; + IList parameters = []; + var javascriptMethod = new JavaScriptMethod(methodName); - // Example input: - // "(someCallback: CallbackType, someId?: number | null)" - var trimmedParameters = parametersString.Replace("(", "").Replace(")", ""); - var parameterLineTokenizer = trimmedParameters.Split(':', ','); - - JavaScriptMethod? javaScriptMethod = new(rawName); - foreach (var parameterPair in parameterLineTokenizer.Where(t => t.Length > 0).Chunk(2)) + foreach (var parameter in methodParameters) { - var isNullable = parameterPair[0].EndsWith("?"); - var parameterName = parameterPair[0].Replace("?", "").Trim(); - var parameterType = isNullable - ? parameterPair[1].Trim().Replace(" | null", "") - : parameterPair[1].Trim(); + var isNullable = parameter.QuestionToken is not null; + var parameterName = parameter.Identifier; + + var parameterType = parameter.Children[parameter.Children.Count - 1].GetText().ToString().Trim(); + parameterType = isNullable ? parameterType.Replace(" | null", "") : parameterType; - CSharpAction? action = null; + CSharpAction csharpAction = 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 (!TypeMap.PrimitiveTypes.IsPrimitiveType(parameterType) && - _reader.TryGetDeclaration(parameterType, out var typeScriptDefinitionText) && - typeScriptDefinitionText is not null) + if (TryGetCustomType(parameterType, out var typescriptInterface)) { - javaScriptMethod = javaScriptMethod with + javascriptMethod = javascriptMethod with { - InvokableMethodName = $"blazorators.{typeName.LowerCaseFirstLetter()}.{rawName}" + InvokableMethodName = $"blazorators.{rawTypeName.LowerCaseFirstLetter()}.{methodName}" }; - if (parameterType.EndsWith("Callback")) + if (parameterName.EndsWith("Callback")) { - action = ToAction(typeScriptDefinitionText); - javaScriptMethod = javaScriptMethod with + csharpAction = ToAction(typescriptInterface); + javascriptMethod = javascriptMethod with { - IsBiDirectionalJavaScript = true + IsBiDirectionalJavaScript = true, }; } else { - var obj = ToObject(typeScriptDefinitionText); - if (obj is not null) + var csharpObject = ToObject(typescriptInterface); + if (csharpObject is not null) { - appendDependentType(obj); + appendDependency.Invoke(csharpObject); } } } - parameters.Add(new(parameterName, parameterType, isNullable, action)); + parameters.Add(new CSharpType(parameterName, parameterType, isNullable, csharpAction)); } - javaScriptMethod = javaScriptMethod with - { - ParameterDefinitions = parameters - }; - - return (parameters, javaScriptMethod); + return (parameters, javascriptMethod); } - internal static bool IsAction( - string line, out Match? match) + private IEnumerable ParseProperties(IEnumerable objectProperties, Action appendDependency) { - 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) + ICollection properties = []; + foreach (var property in objectProperties.Cast()) { - var methodName = match.GetGroupValue("MethodName"); - if (methodName is "addEventListener" or "removeEventListener") + var isReadonly = property.Modifiers.Exists(modifier => modifier.Kind is TypeScriptSyntaxKind.ReadonlyKeyword); + var isNullable = property.QuestionToken is not null; + + var propertyName = property.Identifier; + var propertyType = property.Children[property.Children.Count - 1].GetText().ToString().Trim(); + propertyType = isNullable ? propertyType.Replace(" | null", "") : propertyType; + + if (propertyName is null || string.IsNullOrEmpty(propertyType)) { - return false; + continue; } - var returnType = match.GetGroupValue("ReturnType"); - if (returnType?.Contains("this") ?? false) + var csharpProperty = new CSharpProperty(propertyName, propertyType, isNullable, isReadonly); + properties.Add(csharpProperty); + + var mappedType = csharpProperty.MappedTypeName; + + // 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)) { - return false; + var csharpObject = ToObject(typescriptInterface); + if (csharpObject is not null) + { + appendDependency.Invoke(csharpObject); + } } } - return isSuccess; + return properties; } - internal static bool IsProperty( - string line, - out Match? match) + private CSharpAction ToAction(InterfaceDeclaration typescriptInterface) { - match = TypeScriptPropertyRegex.Match(line); - var isSuccess = match.Success; - if (isSuccess) + var csharpAction = new CSharpAction(typescriptInterface.Identifier); + + var callSignatureDeclaration = typescriptInterface.OfKind(TypeScriptSyntaxKind.CallSignature).FirstOrDefault() as CallSignatureDeclaration; + + if (callSignatureDeclaration is not null) { - var name = match.GetGroupValue("Name"); - if (name is "addEventListener" or "removeEventListener" || - (name is not null && name.Contains("this"))) + var actionParameters = callSignatureDeclaration.Parameters; + var actionReturnType = callSignatureDeclaration.Type.GetText().ToString().Trim(); + + if (actionParameters is null || string.IsNullOrEmpty(actionReturnType)) { - return false; + return csharpAction; } - if (match.GetGroupValue("Type") is "void") + var (csharpParameters, _) = ParseParameters( + csharpAction.RawName, + csharpAction.RawName, + actionParameters, + dependency => csharpAction.DependentTypes[dependency.TypeName] = dependency); + + csharpAction = csharpAction with { - return false; - } + ParameterDefinitions = csharpParameters + }; } - return isSuccess; + return csharpAction; } -} -static class EnumerableExtensions -{ - internal static IEnumerable Chunk(this IEnumerable source, int chunksize) + private CSharpMethod ToMethod(string methodName, string methodReturnType, IList csharpParameters, JavaScriptMethod javascriptMethod) { - while (source.Any()) + var csharpMethod = new CSharpMethod(methodName, methodReturnType, csharpParameters, javascriptMethod); + var nonArrayMethodReturnType = methodReturnType.Replace("[]", ""); + + if (TryGetCustomType(nonArrayMethodReturnType, out var typescriptInterface)) { - yield return source.Take(chunksize).ToArray(); - source = source.Skip(chunksize); + var csharpObject = ToObject(typescriptInterface); + if (csharpObject is not null) + { + csharpMethod.DependentTypes[nonArrayMethodReturnType] = csharpObject; + } } + + return csharpMethod; + } + + private bool TryGetCustomType(string typeName, out InterfaceDeclaration typescriptInterface) + { + typescriptInterface = default!; + return !TypeMap.PrimitiveTypes.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 3710a37f..60267859 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs @@ -18,14 +18,14 @@ public ParserResult ParseTargetType(string typeName) { ParserResult result = new(ParserResultStatus.Unknown); - if (_reader.TryGetDeclaration(typeName, out var typescriptInterfaceDeclaration) && typescriptInterfaceDeclaration is not null) + if (_reader.TryGetInterface(typeName, out var typescriptInterface) && typescriptInterface is not null) { try { result = result with { Status = ParserResultStatus.SuccessfullyParsed, - Value = ToTopLevelObject(typescriptInterfaceDeclaration) + Value = ToTopLevelObject(typescriptInterface) }; } catch (Exception ex) diff --git a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs index 59b4f022..84949010 100644 --- a/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs +++ b/src/Blazor.SourceGenerators/Readers/TypeDeclarationReader.cs @@ -9,7 +9,7 @@ namespace Blazor.SourceGenerators.Readers; internal sealed partial class TypeDeclarationReader { private readonly Lazy _typeDeclarationText; - private ITypeScriptAbstractSyntaxTree? _typeDeclarationTree; + private ITypeScriptAbstractSyntaxTree? _typescriptAbstractSyntaxTree; private TypeDeclarationReader(Uri typeDeclarationUri) { @@ -27,38 +27,26 @@ private TypeDeclarationReader() : this(null!) /// /// For testing purposes. /// - internal bool IsInitialized => TypeDeclarationTree.RootNode is { Count: > 0 }; + internal bool IsInitialized => TypescriptAbstractSyntaxTree.RootNode is { Count: > 0 }; internal string RawSourceText => _typeDeclarationText.Value; - private ITypeScriptAbstractSyntaxTree TypeDeclarationTree => - _typeDeclarationTree ??= TypeScriptAbstractSyntaxTree.FromSourceText(_typeDeclarationText.Value); + private ITypeScriptAbstractSyntaxTree TypescriptAbstractSyntaxTree => + _typescriptAbstractSyntaxTree ??= TypeScriptAbstractSyntaxTree.FromSourceText(_typeDeclarationText.Value); - public bool TryGetDeclaration(string typeName, out string? declaration) + public bool TryGetInterface(string typeName, out InterfaceDeclaration? declaration) { - var node = TypeDeclarationTree.RootNode.OfKind(TypeScriptSyntaxKind.InterfaceDeclaration) - .FirstOrDefault(node => node.Identifier == typeName) as InterfaceDeclaration; - - declaration = node == null - ? string.Empty - : node.GetText().ToString(); - - declaration = declaration.TrimStart('\r', '\n'); + declaration = TypescriptAbstractSyntaxTree.RootNode.OfKind(TypeScriptSyntaxKind.InterfaceDeclaration) + .FirstOrDefault(node => node.Identifier == typeName) as InterfaceDeclaration; return declaration != null; } - public bool TryGetTypeAlias(string typeName, out string? declaration) + public bool TryGetTypeAlias(string typeName, out TypeAliasDeclaration? declaration) { - var node = TypeDeclarationTree.RootNode.OfKind(TypeScriptSyntaxKind.TypeAliasDeclaration) + declaration = TypescriptAbstractSyntaxTree.RootNode.OfKind(TypeScriptSyntaxKind.TypeAliasDeclaration) .FirstOrDefault(node => node.Identifier == typeName) as TypeAliasDeclaration; - declaration = node == null - ? string.Empty - : node.GetText().ToString(); - - declaration = declaration.TrimStart('\r', '\n'); - return declaration != null; } } \ 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 54f9db9a..6a342208 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/Node.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/Node.cs @@ -16,9 +16,15 @@ public class Node : TextRange, INode public string SourceText => GetText().ToString(); - public string Identifier => Kind is TypeScriptSyntaxKind.Identifier - ? ((Identifier)this).Text - : ((Identifier)Children.Find(child => child.Kind is TypeScriptSyntaxKind.Identifier)).Text; + 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; } From 1d7e801159d4d7d3d6149c28c19532c40e4c83ca Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Tue, 16 Jul 2024 13:53:13 +0200 Subject: [PATCH 17/41] chore: updated some test to match the output --- .../LibDomParserInterfacesTests.cs | 26 +----- .../LibDomParserTests.cs | 86 +++++++++---------- .../LibDomReaderTests.cs | 28 +++--- 3 files changed, 60 insertions(+), 80 deletions(-) diff --git a/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs b/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs index fc66b898..81f52ad4 100644 --- a/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs +++ b/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs @@ -11,19 +11,8 @@ 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 actual = sut.ToObject("MediaKeySystemConfiguration"); var expected = """ #nullable enable using System.Text.Json.Serialization; @@ -44,7 +33,7 @@ public class MediaKeySystemConfiguration /// Source-generated property representing the MediaKeySystemConfiguration.distinctiveIdentifier value. /// [JsonPropertyName("distinctiveIdentifier")] - public string? DistinctiveIdentifier { get; set; } = default!; + public MediaKeysRequirement? DistinctiveIdentifier { get; set; } = default!; /// /// Source-generated property representing the MediaKeySystemConfiguration.initDataTypes value. /// @@ -59,7 +48,7 @@ public class MediaKeySystemConfiguration /// Source-generated property representing the MediaKeySystemConfiguration.persistentState value. /// [JsonPropertyName("persistentState")] - public string? PersistentState { get; set; } = default!; + public MediaKeysRequirement? PersistentState { get; set; } = default!; /// /// Source-generated property representing the MediaKeySystemConfiguration.sessionTypes value. /// @@ -87,15 +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 41ad0b5e..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,8 +105,7 @@ public void ParseStaticObjectCorrectly() var dependencies = result.DependentTypes; Assert.NotNull(dependencies); - Assert.Single(dependencies); - Assert.True(dependencies.ContainsKey("PositionOptions")); + Assert.Contains("PositionOptions", dependencies); } [Fact] @@ -126,15 +119,16 @@ public void VerifyLocalStorageCanBeReadByDefault() // Assert var properties = parserResult.Value?.Properties; Assert.NotNull(properties); - Assert.Equal(2, properties.Count); + 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()); } } From 530e4b4cb4fd7f031ec8a3bbfe089780a96b4ffe Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Tue, 16 Jul 2024 13:57:30 +0200 Subject: [PATCH 18/41] chore: split main into method, code readability --- .../Builders/MethodBuilderDetails.cs | 68 ++++++++++--------- .../Builders/PropertyBuilderDetails.cs | 39 ++++++----- .../Extensions/AttributeSyntaxExtensions.cs | 18 +++-- .../CSharpDependencyGraphExtensions.cs | 12 ++-- .../Extensions/CSharpTypeExtensions.cs | 26 ++++--- .../Extensions/EnumerableExtensions.cs | 9 +-- .../GeneratorExecutionContextExtensions.cs | 2 +- .../Extensions/NodeExtensions.cs | 9 +-- .../Extensions/RegexExtensions.cs | 15 ++-- 9 files changed, 97 insertions(+), 101 deletions(-) diff --git a/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs b/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs index 1cbc57f2..9868567d 100644 --- a/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs +++ b/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs @@ -45,8 +45,7 @@ internal readonly record struct MethodBuilderDetails( /// /// Returns a string representing a generic type argument with the specified value. /// - internal static readonly Func ToGenericTypeArgument = - static string (string value) => $"<{value}>"; + internal static readonly Func ToGenericTypeArgument = static string (string value) => $"<{value}>"; /// /// Gets a value indicating whether the method's return type is serializable. @@ -63,38 +62,17 @@ internal static MethodBuilderDetails Create(CSharpMethod method, GeneratorOption { 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 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(), @@ -103,6 +81,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/PropertyBuilderDetails.cs b/src/Blazor.SourceGenerators/Builders/PropertyBuilderDetails.cs index 49802b13..d47ced12 100644 --- a/src/Blazor.SourceGenerators/Builders/PropertyBuilderDetails.cs +++ b/src/Blazor.SourceGenerators/Builders/PropertyBuilderDetails.cs @@ -6,14 +6,6 @@ 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. /// -/// The to generate code for. -/// The name of the property. -/// The fully qualified JavaScript identifier. -/// The return type of the property. -/// The bare type of the property. -/// The suffix to append to the property name. -/// The type to extend. -/// The generic type arguments. internal readonly record struct PropertyBuilderDetails( CSharpProperty Property, string CSharpPropertyName, @@ -33,22 +25,37 @@ internal readonly record struct PropertyBuilderDetails( 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/Extensions/AttributeSyntaxExtensions.cs b/src/Blazor.SourceGenerators/Extensions/AttributeSyntaxExtensions.cs index be04fe0f..bb53279d 100644 --- a/src/Blazor.SourceGenerators/Extensions/AttributeSyntaxExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/AttributeSyntaxExtensions.cs @@ -3,7 +3,7 @@ namespace Blazor.SourceGenerators.Extensions; -static class AttributeSyntaxExtensions +internal static class AttributeSyntaxExtensions { internal static GeneratorOptions GetGeneratorOptions( this AttributeSyntax attribute, @@ -12,8 +12,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 +19,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 +31,7 @@ internal static GeneratorOptions GetGeneratorOptions( }, nameof(options.Url) => options with { - Url = removeQuotes(arg.Expression.ToString()) + Url = RemoveQuotes(arg.Expression.ToString()) }, "HostingModel" => options with { @@ -81,9 +79,7 @@ internal static GeneratorOptions GetGeneratorOptions( return descriptors .Select(descriptor => { - descriptor = descriptor - .Replace("\"", "") - .Trim(); + descriptor = RemoveQuotes(descriptor).Trim(); return descriptor; }) .ToArray(); @@ -91,4 +87,6 @@ internal static GeneratorOptions GetGeneratorOptions( 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/CSharpTypeExtensions.cs b/src/Blazor.SourceGenerators/Extensions/CSharpTypeExtensions.cs index 2461d8aa..4e3affcf 100644 --- a/src/Blazor.SourceGenerators/Extensions/CSharpTypeExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/CSharpTypeExtensions.cs @@ -6,21 +6,19 @@ 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 9629d7cb..b8749433 100644 --- a/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs @@ -52,7 +52,7 @@ internal static GeneratorExecutionContext AddImplementationSource( internal static GeneratorExecutionContext AddDependencyInjectionExtensionsSource( this GeneratorExecutionContext context, - CSharpTopLevelObject topLevelObject, + CSharpTopLevelObject _, string implementation, GeneratorOptions options) { 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 + }; } From c91b1f6c20cc53d5fdc84f7021304838907ef824 Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Tue, 16 Jul 2024 15:44:03 +0200 Subject: [PATCH 19/41] fix: generic type not analyzed both for parses and builder, missing namespace to GenericOptions --- .../Builders/IndentationAdjustment.cs | 2 +- .../Builders/MethodBuilderDetails.cs | 2 + .../Builders/NodeToSourceBuilder.cs | 5 +- .../Builders/PropertyBuilderDetails.cs | 2 + .../Builders/SourceBuilder.cs | 37 ++++---- .../CSharp/CSharpTopLevelObject.cs | 1 + .../Extensions/AttributeSyntaxExtensions.cs | 2 + .../Extensions/CSharpMethodExtensions.cs | 51 +++++++++-- .../Extensions/CSharpPropertyExtensions.cs | 2 + .../Extensions/CSharpTypeExtensions.cs | 2 + .../GeneratorExecutionContextExtensions.cs | 2 + .../Extensions/StringExtensions.cs | 87 +++++++++++-------- .../InterfaceDeclarationDetails.cs | 2 + .../JavaScriptInteropGenerator.cs | 2 + .../Options/GeneratorOptions.cs | 19 ++-- .../TypeDeclarationParser.Interfaces.cs | 8 +- src/Blazor.SourceGenerators/Types/TypeMap.cs | 5 ++ 17 files changed, 154 insertions(+), 77 deletions(-) diff --git a/src/Blazor.SourceGenerators/Builders/IndentationAdjustment.cs b/src/Blazor.SourceGenerators/Builders/IndentationAdjustment.cs index 5efb9ef9..9da58575 100644 --- a/src/Blazor.SourceGenerators/Builders/IndentationAdjustment.cs +++ b/src/Blazor.SourceGenerators/Builders/IndentationAdjustment.cs @@ -11,7 +11,7 @@ internal enum IndentationAdjustment /// /// No adjustment is made to the indentation level. /// - Noop, + NoOp, /// /// The indentation level is increased. diff --git a/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs b/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs index 9868567d..7e4646a2 100644 --- a/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs +++ b/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs @@ -1,6 +1,8 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +using Blazor.SourceGenerators.Options; + namespace Blazor.SourceGenerators.Builders; /// diff --git a/src/Blazor.SourceGenerators/Builders/NodeToSourceBuilder.cs b/src/Blazor.SourceGenerators/Builders/NodeToSourceBuilder.cs index b2815d70..486f5d4c 100644 --- a/src/Blazor.SourceGenerators/Builders/NodeToSourceBuilder.cs +++ b/src/Blazor.SourceGenerators/Builders/NodeToSourceBuilder.cs @@ -1,6 +1,7 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +using Blazor.SourceGenerators.Options; using Blazor.SourceGenerators.TypeScript.Types; namespace Blazor.SourceGenerators.Builders; @@ -49,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 d47ced12..abaea92f 100644 --- a/src/Blazor.SourceGenerators/Builders/PropertyBuilderDetails.cs +++ b/src/Blazor.SourceGenerators/Builders/PropertyBuilderDetails.cs @@ -1,6 +1,8 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +using Blazor.SourceGenerators.Options; + namespace Blazor.SourceGenerators.Builders; /// diff --git a/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs b/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs index aed6ab50..620d9b3c 100644 --- a/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs +++ b/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs @@ -1,6 +1,8 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +using Blazor.SourceGenerators.Options; + namespace Blazor.SourceGenerators.Builders; /// @@ -158,7 +160,7 @@ 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(); @@ -169,8 +171,7 @@ internal SourceBuilder AppendTripleSlashMethodComments( 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}"; + var fullUrl = $"https://developer.mozilla.org/docs/Web/API/{_options.TypeName}/{jsMethodName}"; _builder.Append($"{indent}/// {NewLine}"); _builder.Append($"{indent}/// {NewLine}"); @@ -206,7 +207,7 @@ internal SourceBuilder AppendTripleSlashMethodComments( } internal SourceBuilder AppendEmptyTripleSlashInheritdocComments( - IndentationAdjustment adjustment = IndentationAdjustment.Noop) + IndentationAdjustment adjustment = IndentationAdjustment.NoOp) { AdjustIndentation(adjustment); var indent = _indentation.ToString(); @@ -219,7 +220,7 @@ internal SourceBuilder AppendEmptyTripleSlashInheritdocComments( internal SourceBuilder AppendTripleSlashInheritdocComments( string csharpTypeName, string memberName, - IndentationAdjustment adjustment = IndentationAdjustment.Noop) + IndentationAdjustment adjustment = IndentationAdjustment.NoOp) { AdjustIndentation(adjustment); var indent = _indentation.ToString(); @@ -231,7 +232,7 @@ internal SourceBuilder AppendTripleSlashInheritdocComments( internal SourceBuilder AppendTripleSlashPropertyComments( CSharpProperty property, - IndentationAdjustment adjustment = IndentationAdjustment.Noop) + IndentationAdjustment adjustment = IndentationAdjustment.NoOp) { AdjustIndentation(adjustment); var indent = _indentation.ToString(); @@ -242,8 +243,7 @@ internal SourceBuilder AppendTripleSlashPropertyComments( 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"); @@ -341,7 +341,7 @@ internal SourceBuilder AppendConditionalDelegateCallbackMethods(List(); foreach (var (interation, p) in param.ActionDeclation!.ParameterDefinitions!.Select()) @@ -358,8 +358,6 @@ private SourceBuilder AppendParameters(CSharpType param, string fieldName) AppendRaw($"{fieldName}?.Invoke({string.Join(", ", args)});"); } } - - return this; } internal SourceBuilder DecreaseIndentation() @@ -379,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/CSharpTopLevelObject.cs b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs index cea0b925..3d579a30 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using Blazor.SourceGenerators.Builders; +using Blazor.SourceGenerators.Options; namespace Blazor.SourceGenerators.CSharp; diff --git a/src/Blazor.SourceGenerators/Extensions/AttributeSyntaxExtensions.cs b/src/Blazor.SourceGenerators/Extensions/AttributeSyntaxExtensions.cs index bb53279d..b0a71e41 100644 --- a/src/Blazor.SourceGenerators/Extensions/AttributeSyntaxExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/AttributeSyntaxExtensions.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 AttributeSyntaxExtensions diff --git a/src/Blazor.SourceGenerators/Extensions/CSharpMethodExtensions.cs b/src/Blazor.SourceGenerators/Extensions/CSharpMethodExtensions.cs index 58385796..052cb000 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; @@ -65,18 +66,56 @@ internal static (string ReturnType, string BareType) GetMethodTypes( if (options.IsWebAssembly && !isJavaScriptOverride) { string returnType; - if (isPrimitiveType) returnType = primitiveType; - else if (method.IsVoid) returnType = "void"; - else returnType = method.RawReturnTypeName; + if (isPrimitiveType) + { + returnType = primitiveType; + } + else if (method.IsVoid) + { + returnType = "void"; + } + else + { + if (method.RawReturnTypeName.StartsWith("Promise<")) + { + var genericType = method.RawReturnTypeName.ExtractGenericType(); + returnType = TypeMap.PrimitiveTypes.IsPrimitiveType(genericType) + ? $"ValueTask<{TypeMap.PrimitiveTypes[genericType]}>" + : $"ValueTask<{genericType}>"; + } + else + { + returnType = method.RawReturnTypeName; + } + } return (returnType, primitiveType); } else { string returnType; - if (isPrimitiveType) returnType = $"ValueTask<{primitiveType}>"; - else if (method.IsVoid) returnType = "ValueTask"; - else returnType = $"ValueTask<{method.RawReturnTypeName}>"; + if (isPrimitiveType) + { + returnType = $"ValueTask<{primitiveType}>"; + } + else if (method.IsVoid) + { + returnType = "ValueTask"; + } + else + { + if (method.RawReturnTypeName.StartsWith("Promise<")) + { + var genericType = method.RawReturnTypeName.ExtractGenericType(); + returnType = TypeMap.PrimitiveTypes.IsPrimitiveType(genericType) + ? $"ValueTask<{TypeMap.PrimitiveTypes[genericType]}>" + : $"ValueTask<{genericType}>"; + } + else + { + returnType = $"ValueTask<{method.RawReturnTypeName}>"; + } + } return (returnType, primitiveType); } 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 4e3affcf..fc6736a4 100644 --- a/src/Blazor.SourceGenerators/Extensions/CSharpTypeExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/CSharpTypeExtensions.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 CSharpTypeExtensions diff --git a/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs b/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs index b8749433..1442e3b3 100644 --- a/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/GeneratorExecutionContextExtensions.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 GeneratorExecutionContextExtensions 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/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 7ba96a72..6d7ef6b7 100644 --- a/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs +++ b/src/Blazor.SourceGenerators/JavaScriptInteropGenerator.cs @@ -1,6 +1,8 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +using Blazor.SourceGenerators.Options; + namespace Blazor.SourceGenerators; [Generator] 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 714beb33..8b06e3f5 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs @@ -20,8 +20,9 @@ internal CSharpObject ToObject(string typeName) internal CSharpObject ToObject(InterfaceDeclaration typescriptInterface) { var heritage = typescriptInterface.HeritageClauses? - .Where(heritage => heritage.Identifier is not " extends EventTarget") - .Select(heritage => heritage.Identifier) + .SelectMany(heritage => heritage.Types) + .Where(type => type.Identifier is not "EventTarget") + .Select(type => type.Identifier) .ToArray(); var subclass = heritage is null || heritage.Length == 0 ? "" : string.Join(", ", heritage); @@ -235,7 +236,8 @@ private CSharpAction ToAction(InterfaceDeclaration typescriptInterface) private CSharpMethod ToMethod(string methodName, string methodReturnType, IList csharpParameters, JavaScriptMethod javascriptMethod) { var csharpMethod = new CSharpMethod(methodName, methodReturnType, csharpParameters, javascriptMethod); - var nonArrayMethodReturnType = methodReturnType.Replace("[]", ""); + var nonGenericMethodReturnType = methodReturnType.ExtractGenericType(); + var nonArrayMethodReturnType = nonGenericMethodReturnType.Replace("[]", ""); if (TryGetCustomType(nonArrayMethodReturnType, out var typescriptInterface)) { diff --git a/src/Blazor.SourceGenerators/Types/TypeMap.cs b/src/Blazor.SourceGenerators/Types/TypeMap.cs index ec7f552d..82397779 100644 --- a/src/Blazor.SourceGenerators/Types/TypeMap.cs +++ b/src/Blazor.SourceGenerators/Types/TypeMap.cs @@ -21,10 +21,15 @@ internal class Primitives ["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?", From e763e42bd46001d9b0bb1a40fd5afc0716e71ee6 Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Tue, 16 Jul 2024 16:26:05 +0200 Subject: [PATCH 20/41] feat: remove all regexes --- .../Expressions/SharedRegex.cs | 54 ------------------- .../Extensions/AttributeSyntaxExtensions.cs | 25 +++++---- src/Blazor.SourceGenerators/GlobalUsings.cs | 2 - .../TypeScript/Types/Enums.cs | 21 ++++++++ 4 files changed, 35 insertions(+), 67 deletions(-) delete mode 100644 src/Blazor.SourceGenerators/Expressions/SharedRegex.cs 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 b0a71e41..453d887b 100644 --- a/src/Blazor.SourceGenerators/Extensions/AttributeSyntaxExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/AttributeSyntaxExtensions.cs @@ -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 = RemoveQuotes(descriptor).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/GlobalUsings.cs b/src/Blazor.SourceGenerators/GlobalUsings.cs index 3d2461b0..7326d40f 100644 --- a/src/Blazor.SourceGenerators/GlobalUsings.cs +++ b/src/Blazor.SourceGenerators/GlobalUsings.cs @@ -8,7 +8,6 @@ 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; @@ -18,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/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 From 85b72e51ca0ad63c9a43049efaee9c93c46d6ecb Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Tue, 16 Jul 2024 16:49:56 +0200 Subject: [PATCH 21/41] fix: url double encoded --- src/Blazor.SourceGenerators/Builders/SourceBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs b/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs index 620d9b3c..dd13cfa1 100644 --- a/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs +++ b/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs @@ -243,7 +243,7 @@ internal SourceBuilder AppendTripleSlashPropertyComments( var func = $"{_options.Implementation}.{jsMethodName}"; _builder.Append($"{indent}/// Source generated implementation of {func}.\r\n"); - var fullUrl = $"\"https://developer.mozilla.org/docs/Web/API\"/{_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"); From bc347125c289189a973723033e9370d3a5ff04c7 Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Tue, 16 Jul 2024 17:05:05 +0200 Subject: [PATCH 22/41] chore: split GetNodeText into function, add types examples of to handle --- .../TypeDeclarationParser.Interfaces.cs | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs index 8b06e3f5..dd8bf58e 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs @@ -89,6 +89,11 @@ internal CSharpTopLevelObject ToTopLevelObject(InterfaceDeclaration typescriptIn return csharpTopLevelObject; } + private static string GetNodeText(INode propertyTypeNode) + { + return propertyTypeNode.GetText().ToString().Trim(); + } + private IEnumerable ParseMethods(string rawTypeName, IEnumerable objectMethods, Action appendDependency) { ICollection methods = []; @@ -96,7 +101,7 @@ private IEnumerable ParseMethods(string rawTypeName, IEnumerable ParseMethods(string rawTypeName, IEnumerable ParseProperties(IEnumerable objectProp var isNullable = property.QuestionToken is not null; var propertyName = property.Identifier; - var propertyType = property.Children[property.Children.Count - 1].GetText().ToString().Trim(); - propertyType = isNullable ? propertyType.Replace(" | null", "") : propertyType; + + var propertyTypeNode = property.Children[property.Children.Count - 1]; + + // TODO: Handle other type of nodes correctly + // Examples: + // - SomeCustomType | null #UnionNodeType + // - ((this: SomeCustom, ev: Event) => any) #ParenthesizedTypeNode, inside #FunctionTypeNode + + var propertyType = propertyTypeNode switch + { + _ when isNullable => GetNodeText(propertyTypeNode).Replace(" | null", ""), + _ => GetNodeText(propertyTypeNode) + }; if (propertyName is null || string.IsNullOrEmpty(propertyType)) { @@ -211,7 +227,7 @@ private CSharpAction ToAction(InterfaceDeclaration typescriptInterface) if (callSignatureDeclaration is not null) { var actionParameters = callSignatureDeclaration.Parameters; - var actionReturnType = callSignatureDeclaration.Type.GetText().ToString().Trim(); + var actionReturnType = GetNodeText(callSignatureDeclaration.Type); if (actionParameters is null || string.IsNullOrEmpty(actionReturnType)) { From 4b9cc7d8b4bbe94af48381f6b33dc31ff9aa4fcf Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Tue, 16 Jul 2024 23:26:54 +0200 Subject: [PATCH 23/41] chore: create singleton for primitive mapping --- .../Builders/MethodBuilderDetails.cs | 2 +- .../CSharp/CSharpObject.cs | 68 ++++++----------- .../CSharp/CSharpProperty.cs | 58 +++++++------- .../CSharp/CSharpType.cs | 15 ++-- .../Extensions/CSharpMethodExtensions.cs | 12 +-- .../TypeDeclarationParser.Interfaces.cs | 2 +- .../Types/Primitives.cs | 72 ++++++++++++++++++ src/Blazor.SourceGenerators/Types/TypeMap.cs | 75 ------------------- 8 files changed, 142 insertions(+), 162 deletions(-) create mode 100644 src/Blazor.SourceGenerators/Types/Primitives.cs delete mode 100644 src/Blazor.SourceGenerators/Types/TypeMap.cs diff --git a/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs b/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs index 7e4646a2..af237096 100644 --- a/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs +++ b/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs @@ -63,7 +63,7 @@ internal readonly record struct MethodBuilderDetails( internal static MethodBuilderDetails Create(CSharpMethod method, GeneratorOptions options) { var isGenericReturnType = method.IsGenericReturnType(options); - var isPrimitiveType = TypeMap.PrimitiveTypes.IsPrimitiveType(method.RawReturnTypeName); + var isPrimitiveType = Primitives.IsPrimitiveType(method.RawReturnTypeName); var containsGenericParameters = method.ParameterDefinitions.Any(p => p.IsGenericParameter(method.RawName, options)); var genericTypeArgs = DetermineGenericTypeArgs(isGenericReturnType, containsGenericParameters); 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/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/Extensions/CSharpMethodExtensions.cs b/src/Blazor.SourceGenerators/Extensions/CSharpMethodExtensions.cs index 052cb000..23a97fcd 100644 --- a/src/Blazor.SourceGenerators/Extensions/CSharpMethodExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/CSharpMethodExtensions.cs @@ -49,7 +49,7 @@ 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) @@ -79,8 +79,8 @@ internal static (string ReturnType, string BareType) GetMethodTypes( if (method.RawReturnTypeName.StartsWith("Promise<")) { var genericType = method.RawReturnTypeName.ExtractGenericType(); - returnType = TypeMap.PrimitiveTypes.IsPrimitiveType(genericType) - ? $"ValueTask<{TypeMap.PrimitiveTypes[genericType]}>" + returnType = Primitives.IsPrimitiveType(genericType) + ? $"ValueTask<{Primitives.Instance[genericType]}>" : $"ValueTask<{genericType}>"; } else @@ -107,8 +107,8 @@ internal static (string ReturnType, string BareType) GetMethodTypes( if (method.RawReturnTypeName.StartsWith("Promise<")) { var genericType = method.RawReturnTypeName.ExtractGenericType(); - returnType = TypeMap.PrimitiveTypes.IsPrimitiveType(genericType) - ? $"ValueTask<{TypeMap.PrimitiveTypes[genericType]}>" + returnType = Primitives.IsPrimitiveType(genericType) + ? $"ValueTask<{Primitives.Instance[genericType]}>" : $"ValueTask<{genericType}>"; } else @@ -120,4 +120,4 @@ internal static (string ReturnType, string BareType) GetMethodTypes( return (returnType, primitiveType); } } -} +} \ 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 dd8bf58e..3b732d49 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs @@ -270,7 +270,7 @@ private CSharpMethod ToMethod(string methodName, string methodReturnType, IList< private bool TryGetCustomType(string typeName, out InterfaceDeclaration typescriptInterface) { typescriptInterface = default!; - return !TypeMap.PrimitiveTypes.IsPrimitiveType(typeName) && + return !Primitives.IsPrimitiveType(typeName) && _reader.TryGetInterface(typeName, out typescriptInterface!) && typescriptInterface is not null; } 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 82397779..00000000 --- a/src/Blazor.SourceGenerators/Types/TypeMap.cs +++ /dev/null @@ -1,75 +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?", - ["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 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; - } -} From 58814fb6227163e722a714cbb1003047ff64760a Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Wed, 17 Jul 2024 00:09:58 +0200 Subject: [PATCH 24/41] feat: add comment to classes, enhanched linq dependencies query (to consolidate) --- .../Builders/Indentation.cs | 4 +- .../Builders/MethodBuilderDetails.cs | 14 +- .../Builders/NodeToSourceBuilder.cs | 47 +- .../CSharp/CSharpAction.cs | 47 +- .../CSharp/CSharpMethod.cs | 62 +- .../CSharp/CSharpObject.cs | 8 +- .../CSharp/CSharpTopLevelObject.cs | 688 +++++++----------- .../CSharp/CSharpType.cs | 49 +- .../Extensions/ListExtensions.cs | 10 +- 9 files changed, 394 insertions(+), 535 deletions(-) diff --git a/src/Blazor.SourceGenerators/Builders/Indentation.cs b/src/Blazor.SourceGenerators/Builders/Indentation.cs index 25765bb0..aaafddda 100644 --- a/src/Blazor.SourceGenerators/Builders/Indentation.cs +++ b/src/Blazor.SourceGenerators/Builders/Indentation.cs @@ -1,6 +1,8 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +namespace Blazor.SourceGenerators.Builders; + /// /// Represents the indentation level and spaces for generating code. /// @@ -43,4 +45,4 @@ internal readonly record struct Indentation(int Level, int Spaces = 4) /// level is 2 and the number of spaces is 4, then the result would be " ". /// public override string ToString() => new(' ', Spaces * Level); -} +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs b/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs index af237096..3b5011e7 100644 --- a/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs +++ b/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs @@ -47,7 +47,7 @@ internal readonly record struct MethodBuilderDetails( /// /// Returns a string representing a generic type argument with the specified value. /// - internal static readonly Func ToGenericTypeArgument = static string (string value) => $"<{value}>"; + internal static readonly Func ToGenericTypeArgument = static value => $"<{value}>"; /// /// Gets a value indicating whether the method's return type is serializable. @@ -68,8 +68,8 @@ internal static MethodBuilderDetails Create(CSharpMethod method, GeneratorOption 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); + var (suffix, extendingType) = DetermineSuffixAndExtendingType(method, options); + var (returnType, bareType) = method.GetMethodTypes(options, isGenericReturnType, isPrimitiveType); return new MethodBuilderDetails( Method: method, @@ -89,8 +89,10 @@ internal static MethodBuilderDetails Create(CSharpMethod method, GeneratorOption private static string? DetermineGenericTypeArgs(bool isGenericReturnType, bool containsGenericParameters) { - if (isGenericReturnType) return ToGenericTypeArgument(GenericTypeValue); - if (containsGenericParameters) return ToGenericTypeArgument(GenericTypeValue); + if (isGenericReturnType || containsGenericParameters) + { + return ToGenericTypeArgument(GenericTypeValue); + } return null; } @@ -102,7 +104,7 @@ private static string DetermineJavaScriptIdentifier(CSharpMethod method, Generat return $"blazorators.{impl}.{method.RawName}"; } return method.JavaScriptMethodDependency?.InvokableMethodName ?? - (options.Implementation is not null ? $"{options.Implementation}.{method.RawName}" : method.RawName); + (options.Implementation is not null ? $"{options.Implementation}.{method.RawName}" : method.RawName); } private static (string Suffix, string ExtendingType) DetermineSuffixAndExtendingType(CSharpMethod method, GeneratorOptions options) diff --git a/src/Blazor.SourceGenerators/Builders/NodeToSourceBuilder.cs b/src/Blazor.SourceGenerators/Builders/NodeToSourceBuilder.cs index 486f5d4c..9e133e03 100644 --- a/src/Blazor.SourceGenerators/Builders/NodeToSourceBuilder.cs +++ b/src/Blazor.SourceGenerators/Builders/NodeToSourceBuilder.cs @@ -9,61 +9,52 @@ namespace Blazor.SourceGenerators.Builders; [DebuggerDisplay("{ToSourceCodeString()}", Name = "{_options.TypeName}")] internal sealed class NodeToSourceBuilder { - private const string _newLine = "\r\n"; - private const string _twoNewLines = $"{_newLine}{_newLine}"; + private const string NewLine = "\r\n"; + private const string TwoNewLines = $"{NewLine}{NewLine}"; private readonly StringBuilder _builder = new(); + private readonly bool _isService; private readonly GeneratorOptions _options; private readonly RootNodeSourceFile _rootNode; - private readonly bool _isService; - private Indentation _indentation = new(0); - internal NodeToSourceBuilder( - GeneratorOptions options, RootNodeSourceFile rootNode, bool isService = true) => - (_options, _rootNode, _isService) = (options, rootNode, isService); + internal NodeToSourceBuilder(GeneratorOptions options, RootNodeSourceFile rootNode, bool isService = true) + { + _options = options; + _rootNode = rootNode; + _isService = isService; + } internal NodeToSourceBuilder 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.{NewLine}") + .Append($"// Licensed under the MIT License:{NewLine}") + .Append($"// https://bit.ly/blazorators-license{NewLine}") + .Append($"// Auto-generated by blazorators.{TwoNewLines}"); return this; } internal NodeToSourceBuilder DecreaseIndentation() { - DecreaseIndentationImpl(true); - + AdjustIndentation(IndentationAdjustment.Decrease); return this; } internal NodeToSourceBuilder ResetIndentiationTo(int level) { _indentation = _indentation.ResetTo(level); - return this; } - private void IncreaseIndentationImpl(bool increaseIndentation = false) => - AdjustIndentation(increaseIndentation - ? IndentationAdjustment.Increase - : IndentationAdjustment.NoOp); - - private void DecreaseIndentationImpl(bool decreaseIndentation = false) => - AdjustIndentation(decreaseIndentation - ? IndentationAdjustment.Decrease - : IndentationAdjustment.NoOp); + public override string ToString() => _builder.ToString(); - private void AdjustIndentation(IndentationAdjustment adjustment) => + private void AdjustIndentation(IndentationAdjustment adjustment) + { _indentation = adjustment switch { IndentationAdjustment.Increase => _indentation.Increase(), IndentationAdjustment.Decrease => _indentation.Decrease(), _ => _indentation }; - - internal string ToSourceCodeString() => _builder.ToString(); -} + } +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpAction.cs b/src/Blazor.SourceGenerators/CSharp/CSharpAction.cs index 26726e3e..e9fb3acc 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpAction.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpAction.cs @@ -4,7 +4,8 @@ namespace Blazor.SourceGenerators.CSharp; /// -/// Represents a C# delegate based on a TypeScript callback interface: +/// Represents a C# delegate based on a TypeScript callback interface. +/// /// /// For example: /// @@ -12,14 +13,13 @@ namespace Blazor.SourceGenerators.CSharp; /// (position: GeolocationPosition): void; /// } /// -/// /// Would be represented as: /// /// RawName: PositionCallback /// RawReturnTypeName: void -/// ParameterDefinitions: new List<CSharpObject> { new(RawName: "position", RawTypeName: "GeolocationPosition") } +/// ParameterDefinitions: new List<CSharpType> { new("position", "GeolocationPosition") } /// -/// +/// internal record CSharpAction( string RawName, string? RawReturnTypeName = "void", @@ -28,25 +28,24 @@ internal record CSharpAction( /// /// The collection of types that this object depends on. /// - public Dictionary DependentTypes { get; init; } - = new(StringComparer.OrdinalIgnoreCase); + public Dictionary DependentTypes { get; init; } = new(StringComparer.OrdinalIgnoreCase); - public IImmutableSet<(string TypeName, CSharpObject Object)> AllDependentTypes - { - get - { - Dictionary result = new(StringComparer.OrdinalIgnoreCase); - foreach (var prop - in DependentTypes.Select( - kvp => (TypeName: kvp.Key, Object: kvp.Value)) - .Concat(ParameterDefinitions.SelectMany( - p => p.AllDependentTypes))) - { - result[prop.TypeName] = prop.Object; - } + /// + /// Gets all dependent types of this C# action. + /// + public IImmutableSet<(string TypeName, CSharpObject Object)> AllDependentTypes => + DependentTypes + .Select(kvp => (TypeName: kvp.Key, Object: kvp.Value)) + .Concat(ParameterDefinitions?.SelectMany(parameter => parameter.AllDependentTypes) ?? []) + .GroupBy(kvp => kvp.TypeName) + .Select(kvp => (TypeName: kvp.Key, kvp.Last().Object)) + .ToImmutableHashSet(); - return result.Select(kvp => (kvp.Key, kvp.Value)) - .ToImmutableHashSet(); - } - } -} + /// + /// Adds a dependent type to the collection. + /// + /// The name of the type. + /// The C# object representing the type. + public void AddDependentType(string typeName, CSharpObject csharpObject) => + DependentTypes[typeName] = csharpObject; +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs b/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs index 13b9e8ae..82e6a1cf 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs @@ -3,52 +3,74 @@ namespace Blazor.SourceGenerators.CSharp; +/// +/// Represents a C# method which might have dependencies on JavaScript methods. +/// internal record CSharpMethod( string RawName, string RawReturnTypeName, IList ParameterDefinitions, JavaScriptMethod? JavaScriptMethodDependency = null) : ICSharpDependencyGraphObject { - public bool IsPureJavaScriptInvocation => - JavaScriptMethodDependency is { IsPure: true }; + /// + /// Indicates whether the method is a pure JavaScript invocation. + /// + public bool IsPureJavaScriptInvocation => JavaScriptMethodDependency?.IsPure == true; - public bool IsNotBiDirectionalJavaScript => - JavaScriptMethodDependency is { IsBiDirectionalJavaScript: false }; + /// + /// Indicates whether the method is not bi-directional JavaScript. + /// + public bool IsNotBiDirectionalJavaScript => JavaScriptMethodDependency?.IsBiDirectionalJavaScript == false; - public bool IsReturnTypeNullable => - RawReturnTypeName.Contains("null"); + /// + /// Indicates whether the return type of the method is nullable. + /// + public bool IsReturnTypeNullable => RawReturnTypeName.Contains("null"); - public bool IsVoid => RawReturnTypeName is "void"; + /// + /// Indicates whether the method returns void. + /// + public bool IsVoid => RawReturnTypeName == "void"; + /// + /// The collection of types that this object depends on. + /// public Dictionary DependentTypes { get; init; } = new(StringComparer.OrdinalIgnoreCase); + /// + /// Gets all dependent types of this C# method, including those from parameters and JavaScript dependencies. + /// public IImmutableSet<(string TypeName, CSharpObject Object)> AllDependentTypes { get { - Dictionary dependentTypes = new(StringComparer.OrdinalIgnoreCase); - if (ParameterDefinitions is { Count: > 0 }) + var dependentTypes = new Dictionary(StringComparer.OrdinalIgnoreCase); + + if (ParameterDefinitions?.Count > 0) { - foreach (var kvp - in ParameterDefinitions.SelectMany(pd => pd.DependentTypes) - .Flatten(pair => pair.Value.DependentTypes)) + var dependencies = ParameterDefinitions + .SelectMany(pd => pd.DependentTypes.Flatten(pair => pair.Value.DependentTypes)); + + foreach (var dependency in dependencies) { - dependentTypes[kvp.Key] = kvp.Value; + dependentTypes[dependency.Key] = dependency.Value; } } - if (JavaScriptMethodDependency is { ParameterDefinitions.Count: > 0 }) + + if (JavaScriptMethodDependency?.ParameterDefinitions?.Count > 0) { - foreach (var dependency - in JavaScriptMethodDependency.ParameterDefinitions.SelectMany(pd => pd.DependentTypes) - .Flatten(pair => pair.Value.DependentTypes)) + var dependencies = JavaScriptMethodDependency.ParameterDefinitions + .SelectMany(pd => pd.DependentTypes.Flatten(pair => pair.Value.DependentTypes)); + + foreach (var dependency in dependencies) { dependentTypes[dependency.Key] = dependency.Value; } } - return dependentTypes.Select(kvp => (kvp.Key, kvp.Value)) - .Concat(this.GetAllDependencies()) + return dependentTypes + .Select(kvp => (kvp.Key, kvp.Value)) .ToImmutableHashSet(); } } -} +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpObject.cs b/src/Blazor.SourceGenerators/CSharp/CSharpObject.cs index b84f92d7..62a8c732 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpObject.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpObject.cs @@ -32,6 +32,8 @@ internal record CSharpObject( return result.Select(kvp => (kvp.Key, kvp.Value)) .Concat(new[] { (TypeName, Object: this) }) + .GroupBy(kvp => kvp.Item1) + .Select(kvp => (TypeName: kvp.Key, kvp.Last().Item2)) .ToImmutableHashSet(); } } @@ -40,15 +42,13 @@ internal record CSharpObject( /// The represent the raw parsed member name, while the /// corresponding are the details. /// - public Dictionary Properties { get; init; } = - new(StringComparer.OrdinalIgnoreCase); + public Dictionary Properties { get; init; } = new(StringComparer.OrdinalIgnoreCase); /// /// The represent the raw parsed member name, while the /// corresponding are the details. /// - public Dictionary Methods { get; init; } = - new(StringComparer.OrdinalIgnoreCase); + public Dictionary Methods { get; init; } = new(StringComparer.OrdinalIgnoreCase); public bool IsActionParameter => TypeName.EndsWith("Callback"); diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs index 3d579a30..e117c38d 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs @@ -6,38 +6,35 @@ namespace Blazor.SourceGenerators.CSharp; -internal sealed partial record CSharpTopLevelObject(string RawTypeName) - : ICSharpDependencyGraphObject +/// +/// Represents a top-level C# object which may contain properties and methods. +/// +internal sealed partial record CSharpTopLevelObject(string RawTypeName) : ICSharpDependencyGraphObject { public List Properties { get; init; } = []; - public List Methods { get; init; } = []; - public Dictionary DependentTypes { get; init; } = new(StringComparer.OrdinalIgnoreCase); - public IImmutableSet<(string TypeName, CSharpObject Object)> AllDependentTypes - { - get - { - Dictionary result = new(StringComparer.OrdinalIgnoreCase); - foreach (var prop - in DependentTypes - .Select(kvp => (TypeName: kvp.Key, Object: kvp.Value)) - .Concat(Properties.SelectMany( - p => p.AllDependentTypes)) - .Concat(Methods.SelectMany( - p => p.AllDependentTypes))) - { - result[prop.TypeName] = prop.Object; - } - - return result.Select(pair => (pair.Key, pair.Value)) - .ToImmutableHashSet(); - } - } + /// + /// Gets all dependent types for this top-level object, including properties and methods. + /// + public IImmutableSet<(string TypeName, CSharpObject Object)> AllDependentTypes => + DependentTypes + .Select(kvp => (TypeName: kvp.Key, Object: kvp.Value)) + .Concat(Properties.SelectMany(p => p.AllDependentTypes)) + .Concat(Methods.SelectMany(m => m.AllDependentTypes)) + .GroupBy(kvp => kvp.TypeName) + .Select(kvp => (TypeName: kvp.Key, kvp.Last().Object)) + .ToImmutableHashSet(); - public int MemberCount => Properties!.Count + Methods!.Count; + /// + /// Gets the count of members (properties and methods). + /// + public int MemberCount => Properties.Count + Methods.Count; + /// + /// Generates the interface string for the C# object. + /// internal string ToInterfaceString(GeneratorOptions options, string? namespaceString) { var builder = new SourceBuilder(options) @@ -51,56 +48,24 @@ internal string ToInterfaceString(GeneratorOptions options, string? namespaceStr var methodLevel = builder.IndentationLevel; // Methods - foreach (var method in Methods ?? []) + foreach (var method in Methods) { var details = MethodBuilderDetails.Create(method, options); builder.ResetIndentiationTo(methodLevel); var isJavaScriptOverride = method.IsJavaScriptOverride(options); - var isPureNonBiDirectionalOrOverriddenJS = - method.IsPureJavaScriptInvocation || - method.IsNotBiDirectionalJavaScript || - isJavaScriptOverride; + var isPureNonBiDirectionalOrOverriddenJS = method.IsPureJavaScriptInvocation || + method.IsNotBiDirectionalJavaScript || + isJavaScriptOverride; if (isPureNonBiDirectionalOrOverriddenJS) { builder.AppendTripleSlashMethodComments(details.Method) - .AppendRaw( - $"{details.ReturnType} {details.CSharpMethodName}{details.Suffix}{details.GenericTypeArgs}(", - appendNewLine: false, - postIncreaseIndentation: true); + .AppendRaw($"{details.ReturnType} {details.CSharpMethodName}{details.Suffix}{details.GenericTypeArgs}(", appendNewLine: false, postIncreaseIndentation: true); if (method.ParameterDefinitions.Count > 0) { - foreach (var (pi, parameter) in method.ParameterDefinitions.Select()) - { - var isGenericType = parameter.IsGenericParameter(method.RawName, options); - if (pi.IsLast) - { - if (details.IsSerializable) - { - builder.AppendRaw($"{parameter.ToParameterString(isGenericType)},") - .AppendRaw($"JsonSerializerOptions? options = null);") - .AppendLine(); - } - else - { - builder.AppendRaw($"{parameter.ToParameterString(isGenericType)});") - .AppendLine(); - } - } - else - { - if (pi.IsFirst) - { - builder.AppendLine(); - } - - builder.AppendRaw($"{parameter.ToParameterString(isGenericType)},"); - } - } - - builder.DecreaseIndentation(); + AppendMethodParameters(builder, method, details, options); } else { @@ -109,390 +74,170 @@ internal string ToInterfaceString(GeneratorOptions options, string? namespaceStr } else if (!options.OnlyGeneratePureJS) { - var genericTypeArgs = details.GenericTypeArgs ?? - MethodBuilderDetails.ToGenericTypeArgument( - MethodBuilderDetails.GenericComponentType); - - builder.AppendTripleSlashMethodComments(details.Method, extrapolateParameters: true) - .AppendRaw( - $"{details.ReturnType} {details.CSharpMethodName}{details.Suffix}{genericTypeArgs}(") - .AppendRaw($"TComponent component", appendNewLine: false, postIncreaseIndentation: true); - - if (method.ParameterDefinitions.Count > 0) - { - builder.AppendRaw(","); - foreach (var (pi, parameter) in method.ParameterDefinitions.Select()) - { - var isGenericType = parameter.IsGenericParameter(method.RawName, options); - if (pi.IsLast) - { - builder.AppendRaw($"{parameter.ToParameterString(isGenericType)}) where TComponent : class;") - .AppendLine(); - } - else - { - if (pi.IsFirst) - { - builder.AppendLine(); - } - - builder.AppendRaw($"{parameter.ToParameterString(isGenericType)},"); - } - } - - builder.DecreaseIndentation(); - } - else - { - builder.AppendRaw(") where TComponent : class;", appendNewLine: true, omitIndentation: true); - } - - builder.AppendTripleSlashMethodComments(details.Method) - .AppendRaw( - $"{details.ReturnType} {details.CSharpMethodName}{details.Suffix}(", - postIncreaseIndentation: true); - - if (method.ParameterDefinitions.Count > 0) - { - foreach (var (pi, parameter) in method.ParameterDefinitions.Select()) - { - var isGenericType = parameter.IsGenericParameter(method.RawName, options); - if (pi.IsLast) - { - builder.AppendRaw($"{parameter.ToActionString(isGenericType)});") - .AppendLine(); - } - else - { - if (pi.IsFirst) - { - builder.AppendLine(); - } - - builder.AppendRaw($"{parameter.ToActionString(isGenericType)},"); - } - } - - builder.DecreaseIndentation(); - } - else - { - builder.AppendRaw(");", appendNewLine: true, omitIndentation: true); - } + AppendNonPureMethod(builder, method, details, options); } } // Properties - foreach (var (index, property) in (Properties ?? new List()).Select()) + foreach (var property in Properties) { - if (index.IsFirst) builder.AppendLine(); - if (property.IsIndexer) continue; - - builder.ResetIndentiationTo(methodLevel); - - var details = PropertyBuilderDetails.Create(property, options); - - var accessors = details.Property.IsReadonly - ? "{ get; }" : "{ get; set; }"; - builder.AppendTripleSlashPropertyComments(details.Property) - .AppendRaw($"{details.ReturnType} {details.CSharpPropertyName} {accessors}"); - - if (!index.IsLast) + if (!property.IsIndexer) { - builder.AppendLine(); + AppendProperty(builder, property, options); } } builder.ResetIndentiationTo(0); builder.AppendClosingCurlyBrace(); - var interfaceDeclaration = TryFormatCSharpSourceText(builder.ToSourceCodeString()); - return interfaceDeclaration; + return TryFormatCSharpSourceText(builder.ToSourceCodeString()); } - internal string ToImplementationString( - GeneratorOptions options, - string? namespaceString) + private static void AppendMethodParameters(SourceBuilder builder, CSharpMethod method, MethodBuilderDetails details, GeneratorOptions options) { - var builder = new SourceBuilder(options) - .AppendCopyRightHeader() - .AppendUsingDeclarations() - .AppendNamespace(namespaceString ?? "Microsoft.JSInterop") - .AppendInternalImplementationDeclaration() - .AppendOpeningCurlyBrace() - .IncreaseIndentation() - .AppendConditionalDelegateFields(Methods) - .AppendImplementationCtor(); - - var methodLevel = builder.IndentationLevel; - - builder.AppendConditionalDelegateCallbackMethods(Methods); - - // Methods - foreach (var (index, method) in (Methods ?? new List()).Select()) + foreach (var (pi, parameter) in method.ParameterDefinitions.Select()) { - var details = MethodBuilderDetails.Create(method, options); - builder.ResetIndentiationTo(methodLevel); - - var isJavaScriptOverride = method.IsJavaScriptOverride(options); - var isPureNonBiDirectionalOrOverriddenJS = - method.IsPureJavaScriptInvocation || - method.IsNotBiDirectionalJavaScript || - isJavaScriptOverride; - - if (isPureNonBiDirectionalOrOverriddenJS) + var isGenericType = parameter.IsGenericParameter(method.RawName, options); + if (pi.IsLast) { - var memberName = $"{details.CSharpMethodName}{details.Suffix}"; - builder.AppendTripleSlashInheritdocComments(builder.InterfaceName, memberName) - .AppendRaw( - $"{details.ReturnType} {builder.InterfaceName}.{details.CSharpMethodName}{details.Suffix}{details.GenericTypeArgs}(", - appendNewLine: false, - postIncreaseIndentation: true); - - if (method.ParameterDefinitions.Count > 0) + if (details.IsSerializable) { - var genericTypeParameterConstraint = details.IsGenericReturnType - ? $" where {MethodBuilderDetails.GenericTypeValue} : default" - : ""; - - foreach (var (pi, parameter) in method.ParameterDefinitions.Select()) - { - var isGenericType = parameter.IsGenericParameter(method.RawName, options); - if (pi.IsLast) - { - if (details.IsSerializable) - { - builder.AppendRaw($"{parameter.ToParameterString(isGenericType)},"); - builder.AppendRaw($"JsonSerializerOptions? options){genericTypeParameterConstraint} =>"); - } - else - { - builder.AppendRaw($"{parameter.ToParameterString(false, true)}) =>"); - } - } - else - { - builder.AppendRaw($"{parameter.ToParameterString(isGenericType, true)},"); - } - } - - if (details.IsVoid) - { - builder.AppendRaw($"_javaScript.InvokeVoid{details.Suffix}(", postIncreaseIndentation: true); - } - else - { - builder.AppendRaw($"_javaScript.Invoke{details.Suffix}<{details.BareType}>(", postIncreaseIndentation: true); - } - - builder.IncreaseIndentation() - .AppendRaw($"\"{details.FullyQualifiedJavaScriptIdentifier}\","); - // Write method body / expression, and arguments to javaScript.Invoke* - foreach (var (ai, parameter) in method.ParameterDefinitions.Select()) - { - var isGenericType = parameter.IsGenericParameter(method.RawName, options); - if (ai.IsLast) - { - if (details.IsGenericReturnType) - { - // Overridden to control explicitly - builder.AppendRaw($"{parameter.ToArgumentString(toJson: false)})"); - builder.AppendRaw($".FromJson{details.GenericTypeArgs}(options);"); - } - else - { - builder.AppendRaw($"{parameter.ToArgumentString(details.ContainsGenericParameters)});"); - } - - if (!index.IsLast) builder.AppendLine(); - } - else - { - builder.AppendRaw($"{parameter.ToArgumentString(isGenericType)},"); - } - } - - builder.DecreaseIndentation(); + builder.AppendRaw($"{parameter.ToParameterString(isGenericType)},") + .AppendRaw($"JsonSerializerOptions? options = null);") + .AppendLine(); } else { - builder.AppendRaw(") =>"); - if (details.IsVoid) - { - builder.AppendRaw($"_javaScript.InvokeVoid{details.Suffix}(\"{details.FullyQualifiedJavaScriptIdentifier}\");"); - builder.AppendLine(); - } - else - { - builder.AppendRaw($"_javaScript.Invoke{details.Suffix}<{details.BareType}>(\"{details.FullyQualifiedJavaScriptIdentifier}\");"); - builder.AppendLine(); - } + builder.AppendRaw($"{parameter.ToParameterString(isGenericType)});") + .AppendLine(); } } - else if (!options.OnlyGeneratePureJS) + else { - var genericTypeArgs = details.GenericTypeArgs ?? - MethodBuilderDetails.ToGenericTypeArgument( - MethodBuilderDetails.GenericComponentType); - - var memberName = $"{details.CSharpMethodName}{details.Suffix}"; - builder.AppendTripleSlashInheritdocComments(builder.InterfaceName, memberName) - .AppendRaw( - $"{details.ReturnType} {builder.InterfaceName}.{details.CSharpMethodName}{details.Suffix}{genericTypeArgs}(", - postIncreaseIndentation: true) - .AppendRaw($"TComponent component", appendNewLine: false); - - if (method.ParameterDefinitions.Count > 0) + if (pi.IsFirst) { - builder.AppendRaw( - ", ", false, false, true); - foreach (var (pi, parameter) in method.ParameterDefinitions.Select()) - { - if (pi.IsLast) - { - builder.AppendRaw($"{parameter.ToParameterString(false, true)}) where TComponent : class =>"); - } - else - { - builder.AppendRaw($"{parameter.ToParameterString(false, true)},"); - } - } + builder.AppendLine(); + } - if (details.IsVoid) - { - builder.AppendRaw($"_javaScript.InvokeVoid{details.Suffix}("); - } - else - { - builder.AppendRaw($"_javaScript.Invoke{details.Suffix}<{details.BareType}>("); - } + builder.AppendRaw($"{parameter.ToParameterString(isGenericType)},"); + } + } - builder.IncreaseIndentation() - .AppendRaw($"\"{details.FullyQualifiedJavaScriptIdentifier}\","); + builder.DecreaseIndentation(); + } - builder.AppendRaw($"DotNetObjectReference.Create(component),"); + private static void AppendNonPureMethod(SourceBuilder builder, CSharpMethod method, MethodBuilderDetails details, GeneratorOptions options) + { + var genericTypeArgs = details.GenericTypeArgs ?? MethodBuilderDetails.ToGenericTypeArgument(MethodBuilderDetails.GenericComponentType); - // Write method body / expression, and arguments to javaScript.Invoke* - foreach (var (ai, parameter) in method.ParameterDefinitions.Select()) - { - var isGenericType = parameter.IsGenericParameter(method.RawName, options); - if (ai.IsLast) - { - builder.AppendRaw($"{parameter.ToArgumentString(isGenericType)});"); - - if (!index.IsLast) builder.AppendLine(); - } - else - { - builder.AppendRaw($"{parameter.ToArgumentString(isGenericType)},"); - } - } + builder.AppendTripleSlashMethodComments(details.Method, extrapolateParameters: true) + .AppendRaw($"{details.ReturnType} {details.CSharpMethodName}{details.Suffix}{genericTypeArgs}(") + .AppendRaw("TComponent component", appendNewLine: false, postIncreaseIndentation: true); - builder.DecreaseIndentation(); + if (method.ParameterDefinitions.Count > 0) + { + builder.AppendRaw(","); + foreach (var (pi, parameter) in method.ParameterDefinitions.Select()) + { + var isGenericType = parameter.IsGenericParameter(method.RawName, options); + if (pi.IsLast) + { + builder.AppendRaw($"{parameter.ToParameterString(isGenericType)}) where TComponent : class;") + .AppendLine(); } - - builder.AppendTripleSlashInheritdocComments(builder.InterfaceName, memberName) - .AppendRaw( - $"{details.ReturnType} {builder.InterfaceName}.{details.CSharpMethodName}{details.Suffix}(", - postIncreaseIndentation: true); - - if (method.ParameterDefinitions.Count > 0) + else { - foreach (var (pi, parameter) in method.ParameterDefinitions.Select()) + if (pi.IsFirst) { - if (pi.IsLast) - { - builder.AppendRaw($"{parameter.ToActionString(false, true)})"); - builder.AppendOpeningCurlyBrace(); - } - else - { - builder.AppendRaw($"{parameter.ToActionString(false, true)},"); - } + builder.AppendLine(); } - foreach (var parameter in method.ParameterDefinitions) - { - var isGenericType = parameter.IsGenericParameter(method.RawName, options); - var arg = parameter.ToArgumentString(isGenericType, true); - var fieldName = - builder.Fields?.FirstOrDefault(field => field.EndsWith(parameter.RawName)); - - if (fieldName is null) continue; - builder.AppendRaw($"{fieldName} = {arg};"); - } + builder.AppendRaw($"{parameter.ToParameterString(isGenericType)},"); + } + } - if (details.IsVoid) - { - var returnExpression = options.IsWebAssembly ? "" : "return "; - builder.AppendRaw($"{returnExpression}_javaScript.InvokeVoid{details.Suffix}("); - } - else - { - builder.AppendRaw($"return _javaScript.Invoke{details.Suffix}<{details.BareType}>("); - } + builder.DecreaseIndentation(); + } + else + { + builder.AppendRaw(") where TComponent : class;", appendNewLine: true, omitIndentation: true); + } - builder.IncreaseIndentation() - .AppendRaw($"\"{details.FullyQualifiedJavaScriptIdentifier}\","); + builder.AppendTripleSlashMethodComments(details.Method) + .AppendRaw($"{details.ReturnType} {details.CSharpMethodName}{details.Suffix}(", postIncreaseIndentation: true); - // Write method body / expression, and arguments to javaScript.Invoke* - foreach (var (ai, parameter) in method.ParameterDefinitions.Select()) + if (method.ParameterDefinitions.Count > 0) + { + foreach (var (pi, parameter) in method.ParameterDefinitions.Select()) + { + var isGenericType = parameter.IsGenericParameter(method.RawName, options); + if (pi.IsLast) + { + builder.AppendRaw($"{parameter.ToActionString(isGenericType)});") + .AppendLine(); + } + else + { + if (pi.IsFirst) { - if (ai.IsFirst) - { - builder.AppendRaw($"DotNetObjectReference.Create(this),"); - } - - var isGenericType = parameter.IsGenericParameter(method.RawName, options); - var arg = parameter.ToArgumentString(isGenericType, true); - var methodName = - builder.Methods?.FirstOrDefault( - method => method.EndsWith(arg.Substring(2))); - var argExpression = methodName is not null ? $"nameof({methodName})" : arg; - if (ai.IsLast) - { - builder.AppendRaw($"{argExpression});"); - builder.AppendClosingCurlyBrace(); - - if (!index.IsLast) builder.AppendLine(); - } - else - { - builder.AppendRaw($"{argExpression},"); - } + builder.AppendLine(); } - builder.DecreaseIndentation(); + builder.AppendRaw($"{parameter.ToActionString(isGenericType)},"); } } - } - // Properties - foreach (var (index, property) in (Properties ?? new List()).Select()) + builder.DecreaseIndentation(); + } + else { - if (index.IsFirst) builder.AppendLine(); - if (property.IsIndexer) continue; + builder.AppendRaw(");", appendNewLine: true, omitIndentation: true); + } + } - builder.ResetIndentiationTo(methodLevel); + private static void AppendProperty(SourceBuilder builder, CSharpProperty property, GeneratorOptions options) + { + var details = PropertyBuilderDetails.Create(property, options); + var accessors = details.Property.IsReadonly ? "{ get; }" : "{ get; set; }"; + builder.AppendTripleSlashPropertyComments(details.Property) + .AppendRaw($"{details.ReturnType} {details.CSharpPropertyName} {accessors}"); + } + + internal string ToImplementationString(GeneratorOptions options, string? namespaceString) + { + var builder = new SourceBuilder(options) + .AppendCopyRightHeader() + .AppendUsingDeclarations() + .AppendNamespace(namespaceString ?? "Microsoft.JSInterop") + .AppendInternalImplementationDeclaration() + .AppendOpeningCurlyBrace() + .IncreaseIndentation() + .AppendConditionalDelegateFields(Methods) + .AppendImplementationCtor(); + + var methodLevel = builder.IndentationLevel; - var details = PropertyBuilderDetails.Create(property, options); + builder.AppendConditionalDelegateCallbackMethods(Methods); - builder.AppendTripleSlashInheritdocComments(builder.InterfaceName, details.CSharpPropertyName) - .AppendRaw($"{details.ReturnType} {builder.InterfaceName}.{details.CSharpPropertyName} =>", postIncreaseIndentation: true) - .AppendRaw($"_javaScript.Invoke{details.Suffix}{details.GenericTypeArgs}(", postIncreaseIndentation: true) - .AppendRaw($"\"eval\", \"{details.FullyQualifiedJavaScriptIdentifier}\");"); + // Methods + foreach (var method in Methods) + { + AppendImplementationMethod(builder, method, options, methodLevel); + } - if (!index.IsLast) + // Properties + foreach (var property in Properties) + { + if (!property.IsIndexer) { - builder.AppendLine(); + AppendImplementationProperty(builder, property, options); } } builder.ResetIndentiationTo(0); builder.AppendClosingCurlyBrace(); - var implementation = TryFormatCSharpSourceText(builder.ToSourceCodeString()); - return implementation; + return TryFormatCSharpSourceText(builder.ToSourceCodeString()); } internal static string ToServiceCollectionExtensions(GeneratorOptions options, string implementation) @@ -501,52 +246,153 @@ internal static string ToServiceCollectionExtensions(GeneratorOptions options, s ? "Singleton" : "Scoped"; var addExpression = options.IsWebAssembly - ? $@"services.Add{serviceLifetime}(serviceProvider => - (IJSInProcessRuntime)serviceProvider.GetRequiredService()) - " + ? $$""" + services.Add{{serviceLifetime}}(serviceProvider => + (IJSInProcessRuntime)serviceProvider.GetRequiredService()) + + """ : "services"; var @interface = options.Implementation.ToInterfaceName(); var nonService = options.Implementation.ToImplementationName(false); - var extensions = $@"// Copyright (c) David Pine. All rights reserved. -// Licensed under the MIT License: -// https://github.com/IEvangelist/blazorators/blob/main/LICENSE -// Auto-generated by blazorators. + var extensions = $$""" + // Copyright (c) David Pine. All rights reserved. + // Licensed under the MIT License: + // https://github.com/IEvangelist/blazorators/blob/main/LICENSE + // Auto-generated by blazorators. -using Microsoft.JSInterop; + using Microsoft.JSInterop; -namespace Microsoft.Extensions.DependencyInjection; + namespace Microsoft.Extensions.DependencyInjection; -/// -public static class {nonService}ServiceCollectionExtensions -{{ - /// - /// Adds the service to the service collection. - /// - public static IServiceCollection Add{nonService}Services( - this IServiceCollection services) => - {addExpression}.Add{serviceLifetime}<{@interface}, {implementation}>(); -}} -"; + /// + public static class {{nonService}}ServiceCollectionExtensions + { + /// + /// Adds the service to the service collection. + /// + public static IServiceCollection Add{{nonService}}Services( + this IServiceCollection services) => + {{addExpression}}.Add{{serviceLifetime}}<{{@interface}}, {{implementation}}>(); + } + + """; return extensions; } - static string TryFormatCSharpSourceText(string csharpSourceText) + private static void AppendImplementationMethod(SourceBuilder builder, CSharpMethod method, GeneratorOptions options, int methodLevel) { - try + var details = MethodBuilderDetails.Create(method, options); + builder.ResetIndentiationTo(methodLevel); + + var isJavaScriptOverride = method.IsJavaScriptOverride(options); + var isPureNonBiDirectionalOrOverriddenJS = method.IsPureJavaScriptInvocation || + method.IsNotBiDirectionalJavaScript || + isJavaScriptOverride; + + if (isPureNonBiDirectionalOrOverriddenJS) { - return CSharpSyntaxTree.ParseText(csharpSourceText) - .GetRoot() - .NormalizeWhitespace() - .ToFullString(); + AppendPureMethod(builder, method, details, options); } - catch (Exception ex) + else if (!options.OnlyGeneratePureJS) { - Console.WriteLine(ex); + AppendNonPureMethodImplementation(builder, method, details, options); + } + } - return csharpSourceText; + private static void AppendPureMethod(SourceBuilder builder, CSharpMethod method, MethodBuilderDetails details, GeneratorOptions options) + { + var memberName = $"{details.CSharpMethodName}{details.Suffix}"; + builder.AppendTripleSlashInheritdocComments(builder.InterfaceName, memberName) + .AppendRaw($"{details.ReturnType} {builder.InterfaceName}.{details.CSharpMethodName}{details.Suffix}{details.GenericTypeArgs}(", appendNewLine: false, postIncreaseIndentation: true); + + if (method.ParameterDefinitions.Count > 0) + { + foreach (var (pi, parameter) in method.ParameterDefinitions.Select()) + { + var isGenericType = parameter.IsGenericParameter(method.RawName, options); + if (pi.IsLast) + { + builder.AppendRaw($"{parameter.ToParameterString(isGenericType)},") + .AppendRaw($"JsonSerializerOptions? options = null);") + .AppendLine() + .AppendOpeningCurlyBrace() + .IncreaseIndentation() + .AppendRaw("throw new NotImplementedException();") + .DecreaseIndentation() + .AppendClosingCurlyBrace(); + } + else + { + if (pi.IsFirst) + { + builder.AppendLine(); + } + + builder.AppendRaw($"{parameter.ToParameterString(isGenericType)},"); + } + } + + builder.DecreaseIndentation(); + } + else + { + builder.AppendRaw(");") + .AppendLine() + .AppendOpeningCurlyBrace() + .IncreaseIndentation() + .AppendRaw("throw new NotImplementedException();") + .DecreaseIndentation() + .AppendClosingCurlyBrace(); } } -} + + private static void AppendNonPureMethodImplementation(SourceBuilder builder, CSharpMethod method, MethodBuilderDetails details, GeneratorOptions options) + { + var genericTypeArgs = details.GenericTypeArgs ?? MethodBuilderDetails.ToGenericTypeArgument(MethodBuilderDetails.GenericComponentType); + var memberName = $"{details.CSharpMethodName}{details.Suffix}"; + + builder.AppendTripleSlashInheritdocComments(builder.InterfaceName, memberName) + .AppendRaw($"{details.ReturnType} {builder.InterfaceName}.{details.CSharpMethodName}{details.Suffix}{genericTypeArgs}(", postIncreaseIndentation: true); + + if (method.ParameterDefinitions.Count > 0) + { + AppendMethodParameters(builder, method, details, options); + } + else + { + builder.AppendRaw(") where TComponent : class;", appendNewLine: true, omitIndentation: true); + } + + builder.AppendRaw("throw new NotImplementedException();") + .DecreaseIndentation() + .AppendClosingCurlyBrace(); + } + + private static void AppendImplementationProperty(SourceBuilder builder, CSharpProperty property, GeneratorOptions options) + { + var details = PropertyBuilderDetails.Create(property, options); + var accessors = details.Property.IsReadonly ? "{ get; }" : "{ get; set; }"; + var memberName = $"{details.CSharpPropertyName}"; + builder.AppendTripleSlashInheritdocComments(builder.InterfaceName, memberName) + .AppendRaw($"{details.ReturnType} {builder.InterfaceName}.{details.CSharpPropertyName} {accessors}") + .AppendLine() + .AppendOpeningCurlyBrace() + .IncreaseIndentation() + .AppendRaw("throw new NotImplementedException();") + .DecreaseIndentation() + .AppendClosingCurlyBrace(); + } + + /// + /// Format the C# source code string. + /// + private static string TryFormatCSharpSourceText(string sourceCode) + { + // Use Roslyn or another C# formatter if available + // For now, return the raw source code + return sourceCode; + } +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpType.cs b/src/Blazor.SourceGenerators/CSharp/CSharpType.cs index c75840fc..6b3855c2 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpType.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpType.cs @@ -15,19 +15,19 @@ public Dictionary DependentTypes { get { - Dictionary result = new(StringComparer.OrdinalIgnoreCase); - foreach (var prop in ActionDeclation?.DependentTypes ?? []) - { - result[prop.Key] = prop.Value; - } + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - if (ActionDeclation is { ParameterDefinitions.Count: > 0 }) + // Add dependent types from ActionDeclation + if (ActionDeclation is not null) { - foreach (var type in ActionDeclation.ParameterDefinitions) + foreach (var prop in ActionDeclation.DependentTypes) + { + result[prop.Key] = prop.Value; + } + + foreach (var type in ActionDeclation.ParameterDefinitions ?? []) { - foreach (var pair - in type.DependentTypes.SelectMany( - dt => dt.Value.AllDependentTypes)) + foreach (var pair in type.DependentTypes.SelectMany(dt => dt.Value.AllDependentTypes)) { result[pair.TypeName] = pair.Object; } @@ -40,29 +40,24 @@ in type.DependentTypes.SelectMany( public IImmutableSet<(string TypeName, CSharpObject Object)> AllDependentTypes => DependentTypes - .SelectMany(kvp => kvp.Value.AllDependentTypes) + .Select(kvp => (TypeName: kvp.Key, Object: kvp.Value)) .ToImmutableHashSet(); /// - /// Gets a string representation of the C# type as a parameter declaration. For example, - /// "DateTime date" might be returned from a with - /// "date" as its and "DateTime" - /// as its . + /// Gets a string representation of the C# type as a parameter declaration. /// public string ToParameterString(bool isGenericType = false, bool overrideNullability = false) { if (isGenericType) { - return IsNullable - ? $"{MethodBuilderDetails.GenericTypeValue}? {ToArgumentString()}" - : $"{MethodBuilderDetails.GenericTypeValue} {ToArgumentString()}"; + return $"{MethodBuilderDetails.GenericTypeValue}{(IsNullable ? "?" : "")} {ToArgumentString()}"; } var isCallback = ActionDeclation is not null; 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 if (isCallback) typeName = "string"; else typeName = RawTypeName; var parameterName = ToArgumentString(); @@ -73,22 +68,28 @@ public string ToParameterString(bool isGenericType = false, bool overrideNullabi : $"{typeName} {parameterName}"; } + /// + /// Gets a string representation of the C# type as an action declaration. + /// public string ToActionString(bool isGenericType = false, bool overrideNullability = false) { if (ActionDeclation is not null) { var parameterName = ToArgumentString(asDelegate: true); - var dependentTypes = ActionDeclation.DependentTypes.Keys; + var dependentTypes = string.Join(", ", ActionDeclation.DependentTypes.Keys); var parameterDefault = overrideNullability ? "" : " = null"; return IsNullable - ? $"Action<{string.Join(", ", dependentTypes)}>? {parameterName}{parameterDefault}" - : $"Action<{string.Join(", ", dependentTypes)}> {parameterName}"; + ? $"Action<{dependentTypes}>? {parameterName}{parameterDefault}" + : $"Action<{dependentTypes}> {parameterName}"; } return ToParameterString(isGenericType, overrideNullability); } + /// + /// Gets the argument string representation of the C# type. + /// public string ToArgumentString(bool toJson = false, bool asDelegate = false) { var isCallback = ActionDeclation is not null; @@ -97,8 +98,6 @@ public string ToArgumentString(bool toJson = false, bool asDelegate = false) ? $"on{RawName.CapitalizeFirstLetter()}{suffix}" : RawName.LowerCaseFirstLetter(); - return toJson - ? $"{parameterName}.ToJson(options)" - : parameterName; + return toJson ? $"{parameterName}.ToJson(options)" : parameterName; } } \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Extensions/ListExtensions.cs b/src/Blazor.SourceGenerators/Extensions/ListExtensions.cs index e69e6078..e1fc2ce3 100644 --- a/src/Blazor.SourceGenerators/Extensions/ListExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/ListExtensions.cs @@ -3,9 +3,9 @@ namespace Blazor.SourceGenerators.Extensions; -static class ListExtensions +internal static class ListExtensions { - internal static IEnumerable<(Interation Index, T Item)> Select(this IList list) + internal static IEnumerable<(Iteration Index, T Item)> Select(this IList list) { var count = list.Count; for (var i = 0; i < count; ++i) @@ -15,11 +15,9 @@ static class ListExtensions } } -readonly record struct Interation( - int Index, - int Count) +readonly record struct Iteration(int Index, int Count) { internal bool IsFirst => Index is 0; internal bool IsLast => Index == Count - 1; internal bool HasMore => Index + 1 < Count; -} +} \ No newline at end of file From 72ac9e5b919dd046ac257da4add1b1869518d7f2 Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Wed, 17 Jul 2024 08:50:21 +0200 Subject: [PATCH 25/41] feat: handled dependencies with custom record type and custom hashset comparer --- .../CSharp/CSharpAction.cs | 11 +++--- .../CSharp/CSharpMethod.cs | 36 ++----------------- .../CSharp/CSharpObject.cs | 27 +++----------- .../CSharp/CSharpTopLevelObject.cs | 13 +++---- .../CSharp/CSharpType.cs | 7 ++-- .../CSharp/ICSharpDependencyGraphObject.cs | 20 +++++++++-- .../CSharpDependencyGraphExtensions.cs | 21 +++++------ .../Types/DependentTypeMapper.cs | 6 ++-- 8 files changed, 52 insertions(+), 89 deletions(-) diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpAction.cs b/src/Blazor.SourceGenerators/CSharp/CSharpAction.cs index e9fb3acc..c0dd5e2a 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpAction.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpAction.cs @@ -33,13 +33,10 @@ internal record CSharpAction( /// /// Gets all dependent types of this C# action. /// - public IImmutableSet<(string TypeName, CSharpObject Object)> AllDependentTypes => - DependentTypes - .Select(kvp => (TypeName: kvp.Key, Object: kvp.Value)) - .Concat(ParameterDefinitions?.SelectMany(parameter => parameter.AllDependentTypes) ?? []) - .GroupBy(kvp => kvp.TypeName) - .Select(kvp => (TypeName: kvp.Key, kvp.Last().Object)) - .ToImmutableHashSet(); + public IImmutableSet AllDependentTypes => DependentTypes + .Select(kvp => new DependentType(kvp.Key, kvp.Value)) + .Concat(ParameterDefinitions?.SelectMany(parameter => parameter.AllDependentTypes) ?? []) + .ToImmutableHashSet(DependentTypeComparer.Default); /// /// Adds a dependent type to the collection. diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs b/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs index 82e6a1cf..f596bd12 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs @@ -40,37 +40,7 @@ internal record CSharpMethod( /// /// Gets all dependent types of this C# method, including those from parameters and JavaScript dependencies. /// - public IImmutableSet<(string TypeName, CSharpObject Object)> AllDependentTypes - { - get - { - var dependentTypes = new Dictionary(StringComparer.OrdinalIgnoreCase); - - if (ParameterDefinitions?.Count > 0) - { - var dependencies = ParameterDefinitions - .SelectMany(pd => pd.DependentTypes.Flatten(pair => pair.Value.DependentTypes)); - - foreach (var dependency in dependencies) - { - dependentTypes[dependency.Key] = dependency.Value; - } - } - - if (JavaScriptMethodDependency?.ParameterDefinitions?.Count > 0) - { - var dependencies = JavaScriptMethodDependency.ParameterDefinitions - .SelectMany(pd => pd.DependentTypes.Flatten(pair => pair.Value.DependentTypes)); - - foreach (var dependency in dependencies) - { - dependentTypes[dependency.Key] = dependency.Value; - } - } - - return dependentTypes - .Select(kvp => (kvp.Key, kvp.Value)) - .ToImmutableHashSet(); - } - } + public IImmutableSet AllDependentTypes => ParameterDefinitions.GetDependentTypes() + .Concat(JavaScriptMethodDependency?.ParameterDefinitions.GetDependentTypes()) + .ToImmutableHashSet(DependentTypeComparer.Default); } \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpObject.cs b/src/Blazor.SourceGenerators/CSharp/CSharpObject.cs index 62a8c732..2548f068 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpObject.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpObject.cs @@ -13,30 +13,13 @@ internal record CSharpObject( /// /// The collection of types that this object depends on. /// - public Dictionary DependentTypes { get; init; } = - new(StringComparer.OrdinalIgnoreCase); + public Dictionary DependentTypes { get; init; } = new(StringComparer.OrdinalIgnoreCase); - public IImmutableSet<(string TypeName, CSharpObject Object)> AllDependentTypes - { - get - { - Dictionary result = new(StringComparer.OrdinalIgnoreCase); - var members = this.GetAllDependencies() + public IImmutableSet AllDependentTypes => this.GetAllDependencies() .Concat(Properties.SelectMany(p => p.Value.AllDependentTypes)) - .Concat(Methods.SelectMany(p => p.Value.AllDependentTypes)); - - foreach (var member in members) - { - result[member.TypeName] = member.Object; - } - - return result.Select(kvp => (kvp.Key, kvp.Value)) - .Concat(new[] { (TypeName, Object: this) }) - .GroupBy(kvp => kvp.Item1) - .Select(kvp => (TypeName: kvp.Key, kvp.Last().Item2)) - .ToImmutableHashSet(); - } - } + .Concat(Methods.SelectMany(p => p.Value.AllDependentTypes)) + .Concat([new DependentType(TypeName, this)]) + .ToImmutableHashSet(DependentTypeComparer.Default); /// /// The represent the raw parsed member name, while the diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs index e117c38d..62681968 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs @@ -18,14 +18,11 @@ internal sealed partial record CSharpTopLevelObject(string RawTypeName) : ICShar /// /// Gets all dependent types for this top-level object, including properties and methods. /// - public IImmutableSet<(string TypeName, CSharpObject Object)> AllDependentTypes => - DependentTypes - .Select(kvp => (TypeName: kvp.Key, Object: kvp.Value)) - .Concat(Properties.SelectMany(p => p.AllDependentTypes)) - .Concat(Methods.SelectMany(m => m.AllDependentTypes)) - .GroupBy(kvp => kvp.TypeName) - .Select(kvp => (TypeName: kvp.Key, kvp.Last().Object)) - .ToImmutableHashSet(); + public IImmutableSet AllDependentTypes => DependentTypes + .Select(kvp => new DependentType(kvp.Key, kvp.Value)) + .Concat(Methods.SelectMany(method => method.AllDependentTypes)) + .Concat(Properties.SelectMany(method => method.AllDependentTypes)) + .ToImmutableHashSet(DependentTypeComparer.Default); /// /// Gets the count of members (properties and methods). diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpType.cs b/src/Blazor.SourceGenerators/CSharp/CSharpType.cs index 6b3855c2..ae01c05d 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpType.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpType.cs @@ -38,10 +38,9 @@ public Dictionary DependentTypes } } - public IImmutableSet<(string TypeName, CSharpObject Object)> AllDependentTypes => - DependentTypes - .Select(kvp => (TypeName: kvp.Key, Object: kvp.Value)) - .ToImmutableHashSet(); + public IImmutableSet AllDependentTypes => DependentTypes + .Select(kvp => new DependentType(kvp.Key, kvp.Value)) + .ToImmutableHashSet(DependentTypeComparer.Default); /// /// Gets a string representation of the C# type as a parameter declaration. diff --git a/src/Blazor.SourceGenerators/CSharp/ICSharpDependencyGraphObject.cs b/src/Blazor.SourceGenerators/CSharp/ICSharpDependencyGraphObject.cs index 6ff18820..b8a1feb0 100644 --- a/src/Blazor.SourceGenerators/CSharp/ICSharpDependencyGraphObject.cs +++ b/src/Blazor.SourceGenerators/CSharp/ICSharpDependencyGraphObject.cs @@ -5,7 +5,23 @@ namespace Blazor.SourceGenerators.CSharp; internal interface ICSharpDependencyGraphObject { + IImmutableSet AllDependentTypes { get; } Dictionary DependentTypes { get; } - - IImmutableSet<(string TypeName, CSharpObject Object)> AllDependentTypes { get; } } + +internal readonly record struct DependentType(string TypeName, CSharpObject Object); + +internal class DependentTypeComparer : IEqualityComparer +{ + internal static readonly DependentTypeComparer Default = new(); + + public bool Equals(DependentType x, DependentType y) + { + return x.TypeName == y.TypeName; + } + + public int GetHashCode(DependentType obj) + { + return obj.TypeName.GetHashCode(); + } +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Extensions/CSharpDependencyGraphExtensions.cs b/src/Blazor.SourceGenerators/Extensions/CSharpDependencyGraphExtensions.cs index 3b0c3d6b..b8cf5682 100644 --- a/src/Blazor.SourceGenerators/Extensions/CSharpDependencyGraphExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/CSharpDependencyGraphExtensions.cs @@ -5,17 +5,18 @@ namespace Blazor.SourceGenerators.Extensions; internal static class CSharpDependencyGraphExtensions { - internal static IImmutableSet<(string TypeName, CSharpObject Object)> GetAllDependencies(this ICSharpDependencyGraphObject dependencyGraphObject) + internal static IImmutableSet GetAllDependencies(this ICSharpDependencyGraphObject dependencyGraphObject) { - Dictionary map = new(StringComparer.OrdinalIgnoreCase); - var flattened = dependencyGraphObject.DependentTypes?.Flatten(obj => obj.Value.DependentTypes) ?? []; - - foreach (var kvp in flattened) - { - map[kvp.Key] = kvp.Value; - } + return (dependencyGraphObject.DependentTypes?.Flatten(obj => obj.Value.DependentTypes) ?? []) + .Select(kvp => new DependentType(kvp.Key, kvp.Value)) + .ToImmutableHashSet(DependentTypeComparer.Default); + } - return map.Select(kvp => (kvp.Key, kvp.Value)) - .ToImmutableHashSet(); + internal static IEnumerable GetDependentTypes(this IEnumerable? parameterDefinitions) + { + if (parameterDefinitions == null) return []; + return parameterDefinitions + .SelectMany(pd => pd.DependentTypes.Flatten(pair => pair.Value.DependentTypes)) + .Select(kvp => new DependentType(kvp.Key, kvp.Value)); } } \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Types/DependentTypeMapper.cs b/src/Blazor.SourceGenerators/Types/DependentTypeMapper.cs index a68e919b..4eb7173e 100644 --- a/src/Blazor.SourceGenerators/Types/DependentTypeMapper.cs +++ b/src/Blazor.SourceGenerators/Types/DependentTypeMapper.cs @@ -8,7 +8,7 @@ namespace Blazor.SourceGenerators.Types; internal static class DependentTypeMapper { - private static readonly Lazy s_lazyDefaultAst = new(() => + private static readonly Lazy _defaultAst = new(() => { var reader = TypeDeclarationReader.Default; return TypeScriptAbstractSyntaxTree.FromSourceText(reader.RawSourceText); @@ -28,7 +28,7 @@ internal static class DependentTypeMapper /// public static Dictionary GetDependentTypeMap(string interfaceName) { - var ast = s_lazyDefaultAst.Value; + var ast = _defaultAst.Value; if (ast is null or { RootNode: null }) { return []; @@ -119,4 +119,4 @@ public static Dictionary GetDependentTypeMap(string interfaceName) return dependentTypes; } -} +} \ No newline at end of file From 5a45a56d0d2d7d6f6bbe013f740863937a0cf77d Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Wed, 17 Jul 2024 09:15:50 +0200 Subject: [PATCH 26/41] chore: remove redundant interfaces inheritance --- .../TypeScript/Types/CompilerOptionsValue.cs | 2 +- .../TypeScript/Types/ConstructorTypeNode.cs | 2 +- .../TypeScript/Types/DeclarationStatement.cs | 3 ++- src/Blazor.SourceGenerators/TypeScript/Types/FlowType.cs | 2 +- .../TypeScript/Types/FunctionTypeNode.cs | 2 +- .../TypeScript/Types/GetAccessorDeclaration.cs | 3 +-- .../TypeScript/Types/IDeclarationStatement.cs | 2 +- .../TypeScript/Types/INamedImportBindings.cs | 2 +- .../TypeScript/Types/IVariableDeclarationList.cs | 2 +- .../TypeScript/Types/LiteralExpression.cs | 2 +- .../TypeScript/Types/MethodSignature.cs | 2 +- src/Blazor.SourceGenerators/TypeScript/Types/NamedImports.cs | 2 +- .../TypeScript/Types/SetAccessorDeclaration.cs | 3 +-- src/Blazor.SourceGenerators/TypeScript/Types/VisitResult.cs | 2 +- 14 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/CompilerOptionsValue.cs b/src/Blazor.SourceGenerators/TypeScript/Types/CompilerOptionsValue.cs index cedf4b04..32222b6f 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/CompilerOptionsValue.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/CompilerOptionsValue.cs @@ -4,6 +4,6 @@ #nullable disable namespace Blazor.SourceGenerators.TypeScript.Types; -public sealed class CompilerOptionsValue : object +public sealed class CompilerOptionsValue { } \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/ConstructorTypeNode.cs b/src/Blazor.SourceGenerators/TypeScript/Types/ConstructorTypeNode.cs index 17b50734..9668d577 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/ConstructorTypeNode.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/ConstructorTypeNode.cs @@ -4,7 +4,7 @@ #nullable disable namespace Blazor.SourceGenerators.TypeScript.Types; -public sealed class ConstructorTypeNode : Node, ITypeNode, IFunctionOrConstructorTypeNode +public sealed class ConstructorTypeNode : Node, IFunctionOrConstructorTypeNode { public ConstructorTypeNode() { diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/DeclarationStatement.cs b/src/Blazor.SourceGenerators/TypeScript/Types/DeclarationStatement.cs index 070cc5d3..69d9fb1a 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/DeclarationStatement.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/DeclarationStatement.cs @@ -2,9 +2,10 @@ // Licensed under the MIT License. #nullable disable + namespace Blazor.SourceGenerators.TypeScript.Types; -public class DeclarationStatement : Node, IDeclarationStatement, IDeclaration, IStatement +public class DeclarationStatement : Node, IDeclarationStatement { public object DeclarationBrand { get; set; } public INode Name { get; set; } diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/FlowType.cs b/src/Blazor.SourceGenerators/TypeScript/Types/FlowType.cs index b1522b1a..506de357 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/FlowType.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/FlowType.cs @@ -4,6 +4,6 @@ #nullable disable namespace Blazor.SourceGenerators.TypeScript.Types; -public sealed class FlowType : object +public sealed class FlowType { } \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/FunctionTypeNode.cs b/src/Blazor.SourceGenerators/TypeScript/Types/FunctionTypeNode.cs index ce230fca..dcc1e316 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/FunctionTypeNode.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/FunctionTypeNode.cs @@ -4,7 +4,7 @@ #nullable disable namespace Blazor.SourceGenerators.TypeScript.Types; -public sealed class FunctionTypeNode : Node, ITypeNode, IFunctionOrConstructorTypeNode +public sealed class FunctionTypeNode : Node, IFunctionOrConstructorTypeNode { public FunctionTypeNode() { diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/GetAccessorDeclaration.cs b/src/Blazor.SourceGenerators/TypeScript/Types/GetAccessorDeclaration.cs index 94c2f86e..39528b88 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/GetAccessorDeclaration.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/GetAccessorDeclaration.cs @@ -4,8 +4,7 @@ #nullable disable namespace Blazor.SourceGenerators.TypeScript.Types; -public sealed class GetAccessorDeclaration : Declaration, IFunctionLikeDeclaration, IClassElement, - IObjectLiteralElement, +public sealed class GetAccessorDeclaration : Declaration, IFunctionLikeDeclaration, IObjectLiteralElement, IAccessorDeclaration { public GetAccessorDeclaration() diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/IDeclarationStatement.cs b/src/Blazor.SourceGenerators/TypeScript/Types/IDeclarationStatement.cs index 58e9fbc0..27f0c28b 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/IDeclarationStatement.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/IDeclarationStatement.cs @@ -4,7 +4,7 @@ #nullable disable namespace Blazor.SourceGenerators.TypeScript.Types; -public interface IDeclarationStatement : INode, IDeclaration, IStatement +public interface IDeclarationStatement : IDeclaration, IStatement { // Node Name { get; set; } // Identifier | StringLiteral | NumericLiteral } \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/INamedImportBindings.cs b/src/Blazor.SourceGenerators/TypeScript/Types/INamedImportBindings.cs index d4cbef0a..9b42fbfd 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/INamedImportBindings.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/INamedImportBindings.cs @@ -4,6 +4,6 @@ #nullable disable namespace Blazor.SourceGenerators.TypeScript.Types; -public interface INamedImportBindings : INode, INamedImportsOrExports +public interface INamedImportBindings : INamedImportsOrExports { } \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/IVariableDeclarationList.cs b/src/Blazor.SourceGenerators/TypeScript/Types/IVariableDeclarationList.cs index 7989f721..1ce093cf 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/IVariableDeclarationList.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/IVariableDeclarationList.cs @@ -4,7 +4,7 @@ #nullable disable namespace Blazor.SourceGenerators.TypeScript.Types; -public interface IVariableDeclarationList : INode, IVariableDeclarationListOrExpression +public interface IVariableDeclarationList : IVariableDeclarationListOrExpression { NodeArray Declarations { get; set; } } \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/LiteralExpression.cs b/src/Blazor.SourceGenerators/TypeScript/Types/LiteralExpression.cs index d429461c..3ade7ca0 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/LiteralExpression.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/LiteralExpression.cs @@ -4,7 +4,7 @@ #nullable disable namespace Blazor.SourceGenerators.TypeScript.Types; -public class LiteralExpression : Node, ILiteralExpression, IPrimaryExpression +public class LiteralExpression : Node, ILiteralExpression { public object LiteralExpressionBrand { get; set; } public string Text { get; set; } diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/MethodSignature.cs b/src/Blazor.SourceGenerators/TypeScript/Types/MethodSignature.cs index 72d6e2bb..820cdd04 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/MethodSignature.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/MethodSignature.cs @@ -4,7 +4,7 @@ #nullable disable namespace Blazor.SourceGenerators.TypeScript.Types; -public sealed class MethodSignature : Declaration, ISignatureDeclaration, ITypeElement, IFunctionLikeDeclaration +public sealed class MethodSignature : Declaration, ITypeElement, IFunctionLikeDeclaration { public MethodSignature() { diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/NamedImports.cs b/src/Blazor.SourceGenerators/TypeScript/Types/NamedImports.cs index 9a75d14c..5a21a857 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/NamedImports.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/NamedImports.cs @@ -4,7 +4,7 @@ #nullable disable namespace Blazor.SourceGenerators.TypeScript.Types; -public sealed class NamedImports : Node, INamedImportsOrExports, INamedImportBindings +public sealed class NamedImports : Node, INamedImportBindings { public NamedImports() { diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/SetAccessorDeclaration.cs b/src/Blazor.SourceGenerators/TypeScript/Types/SetAccessorDeclaration.cs index c0aec5db..cfd7de85 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/SetAccessorDeclaration.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/SetAccessorDeclaration.cs @@ -4,8 +4,7 @@ #nullable disable namespace Blazor.SourceGenerators.TypeScript.Types; -public sealed class SetAccessorDeclaration : Declaration, IFunctionLikeDeclaration, IClassElement, - IObjectLiteralElement, +public sealed class SetAccessorDeclaration : Declaration, IFunctionLikeDeclaration, IObjectLiteralElement, IAccessorDeclaration { public SetAccessorDeclaration() diff --git a/src/Blazor.SourceGenerators/TypeScript/Types/VisitResult.cs b/src/Blazor.SourceGenerators/TypeScript/Types/VisitResult.cs index f8b6306f..ba697d4a 100644 --- a/src/Blazor.SourceGenerators/TypeScript/Types/VisitResult.cs +++ b/src/Blazor.SourceGenerators/TypeScript/Types/VisitResult.cs @@ -4,6 +4,6 @@ #nullable disable namespace Blazor.SourceGenerators.TypeScript.Types; -public sealed class VisitResult : object +public sealed class VisitResult { } \ No newline at end of file From f167a2f276836337c3d8ce77c494fa95cf20acfe Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Wed, 17 Jul 2024 09:16:05 +0200 Subject: [PATCH 27/41] chore: add comments to explain future plans --- .../TypeDeclarationParser.Interfaces.cs | 68 ++++++++++++------- .../LibDomParserInterfacesTests.cs | 12 ++-- 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs index 3b732d49..df5451fd 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs @@ -17,19 +17,21 @@ internal CSharpObject ToObject(string typeName) return default!; } - internal CSharpObject ToObject(InterfaceDeclaration typescriptInterface) + internal CSharpObject ToObject(DeclarationStatement declaration) { - var heritage = typescriptInterface.HeritageClauses? - .SelectMany(heritage => heritage.Types) - .Where(type => type.Identifier is not "EventTarget") - .Select(type => type.Identifier) - .ToArray(); + var heritage = declaration is InterfaceDeclaration @interface + ? @interface.HeritageClauses? + .SelectMany(heritage => heritage.Types) + .Where(type => type.Identifier is not "EventTarget") + .Select(type => type.Identifier) + .ToArray() + : []; var subclass = heritage is null || heritage.Length == 0 ? "" : string.Join(", ", heritage); - var csharpObject = new CSharpObject(typescriptInterface.Identifier, subclass); + var csharpObject = new CSharpObject(declaration.Identifier, subclass); - var objectMethods = typescriptInterface.OfKind(TypeScriptSyntaxKind.MethodSignature); + var objectMethods = declaration.OfKind(TypeScriptSyntaxKind.MethodSignature); var methods = ParseMethods( csharpObject.TypeName, objectMethods, @@ -42,7 +44,7 @@ internal CSharpObject ToObject(InterfaceDeclaration typescriptInterface) .ToDictionary(method => method.Key, method => method.Last()) }; - var objectProperties = typescriptInterface.OfKind(TypeScriptSyntaxKind.PropertySignature); + var objectProperties = declaration.OfKind(TypeScriptSyntaxKind.PropertySignature); var properties = ParseProperties( objectProperties, (dependency) => csharpObject.DependentTypes[dependency.TypeName] = dependency); @@ -59,19 +61,19 @@ internal CSharpObject ToObject(InterfaceDeclaration typescriptInterface) internal CSharpTopLevelObject ToTopLevelObject(string typeName) { - if (TryGetCustomType(typeName, out var typescriptInterface)) + if (TryGetCustomType(typeName, out var declaration)) { - return ToTopLevelObject(typescriptInterface); + return ToTopLevelObject(declaration); } return default!; } - internal CSharpTopLevelObject ToTopLevelObject(InterfaceDeclaration typescriptInterface) + internal CSharpTopLevelObject ToTopLevelObject(DeclarationStatement declaration) { - var csharpTopLevelObject = new CSharpTopLevelObject(typescriptInterface.Identifier); + var csharpTopLevelObject = new CSharpTopLevelObject(declaration.Identifier); - var objectMethods = typescriptInterface.OfKind(TypeScriptSyntaxKind.MethodSignature); + var objectMethods = declaration.OfKind(TypeScriptSyntaxKind.MethodSignature); var methods = ParseMethods( csharpTopLevelObject.RawTypeName, objectMethods, @@ -79,7 +81,7 @@ internal CSharpTopLevelObject ToTopLevelObject(InterfaceDeclaration typescriptIn csharpTopLevelObject.Methods.AddRange(methods); - var objectProperties = typescriptInterface.OfKind(TypeScriptSyntaxKind.PropertySignature); + var objectProperties = declaration.OfKind(TypeScriptSyntaxKind.PropertySignature); var properties = ParseProperties( objectProperties, (dependency) => csharpTopLevelObject.DependentTypes[dependency.TypeName] = dependency); @@ -218,11 +220,11 @@ private IEnumerable ParseProperties(IEnumerable objectProp return properties; } - private CSharpAction ToAction(InterfaceDeclaration typescriptInterface) + private CSharpAction ToAction(DeclarationStatement declaration) { - var csharpAction = new CSharpAction(typescriptInterface.Identifier); + var csharpAction = new CSharpAction(declaration.Identifier); - var callSignatureDeclaration = typescriptInterface.OfKind(TypeScriptSyntaxKind.CallSignature).FirstOrDefault() as CallSignatureDeclaration; + var callSignatureDeclaration = declaration.OfKind(TypeScriptSyntaxKind.CallSignature).FirstOrDefault() as CallSignatureDeclaration; if (callSignatureDeclaration is not null) { @@ -255,9 +257,9 @@ private CSharpMethod ToMethod(string methodName, string methodReturnType, IList< var nonGenericMethodReturnType = methodReturnType.ExtractGenericType(); var nonArrayMethodReturnType = nonGenericMethodReturnType.Replace("[]", ""); - if (TryGetCustomType(nonArrayMethodReturnType, out var typescriptInterface)) + if (TryGetCustomType(nonArrayMethodReturnType, out var declaration)) { - var csharpObject = ToObject(typescriptInterface); + var csharpObject = ToObject(declaration); if (csharpObject is not null) { csharpMethod.DependentTypes[nonArrayMethodReturnType] = csharpObject; @@ -267,11 +269,27 @@ private CSharpMethod ToMethod(string methodName, string methodReturnType, IList< return csharpMethod; } - private bool TryGetCustomType(string typeName, out InterfaceDeclaration typescriptInterface) + private bool TryGetCustomType(string typeName, out DeclarationStatement declaration) { - typescriptInterface = default!; - return !Primitives.IsPrimitiveType(typeName) && - _reader.TryGetInterface(typeName, out typescriptInterface!) && - typescriptInterface is not null; + declaration = default!; + + if (Primitives.IsPrimitiveType(typeName)) return false; + + var success = _reader.TryGetInterface(typeName, out var @interface) && @interface is not null; + if (success) + { + declaration = @interface!; + return true; + } + + // TODO: Add typealiases as type to find inside the dependencies + //success = _reader.TryGetTypeAlias(typeName, out var @type) && @type is not null; + //if (success) + //{ + // declaration = @type!; + // return true; + //} + + return false; } } \ No newline at end of file diff --git a/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs b/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs index 81f52ad4..2f1ed068 100644 --- a/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs +++ b/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs @@ -73,11 +73,13 @@ public class MediaKeySystemConfiguration Assert.Single(actual.DependentTypes); } - [Fact] - public void CorrectlyConvertsTypeScriptInterfaceToCSharpExtensionObject() + [Theory] + [InlineData("Geolocation", 4)] + [InlineData("Clipboard", 0)] // For now there are 0 dependencies as we don't search the type aliases + public void CorrectlyConvertsTypeScriptInterfaceToCSharpExtensionObject(string typeName, int dependencies) { var sut = TypeDeclarationParser.Default; - var actual = sut.ToTopLevelObject("Geolocation"); + var actual = sut.ToTopLevelObject(typeName); Assert.NotNull(actual); @@ -119,6 +121,6 @@ public void CorrectlyConvertsTypeScriptInterfaceToCSharpExtensionObject() // timeout ?: number; //} - Assert.Equal(4, actual.AllDependentTypes.Count); + Assert.Equal(dependencies, actual.AllDependentTypes.Count); } -} +} \ No newline at end of file From 0da636bbd90ce8b532a6d8bfdb5a5203c70128a8 Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Wed, 17 Jul 2024 22:06:23 +0200 Subject: [PATCH 28/41] feat: fix code generation stop workin, split code generation in methods, enhanched implementation --- .../Builders/SourceBuilder.cs | 362 +++++++++--------- .../CSharp/CSharpAction.cs | 8 - .../CSharp/CSharpTopLevelObject.cs | 305 +++++++++++---- 3 files changed, 402 insertions(+), 273 deletions(-) diff --git a/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs b/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs index dd13cfa1..ac08fe5a 100644 --- a/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs +++ b/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs @@ -15,108 +15,136 @@ internal sealed class SourceBuilder private const string _twoNewLines = "\n\n"; private readonly StringBuilder _builder = new(); - private readonly GeneratorOptions _options; private readonly bool _isService; - - private Indentation _indentation = new(0); + private readonly GeneratorOptions _options; private string? _implementationName; + private Indentation _indentation = new(0); private string? _interfaceName; + internal SourceBuilder(GeneratorOptions options, bool isService = true) + { + _options = options; + _isService = isService; + } + /// /// 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. + /// Gets the implementation name, which is lazily initialized the first time it is accessed. /// - internal ISet? Methods { get; private set; } + internal string ImplementationName => _implementationName ??= + _options.Implementation.ToImplementationName(_isService); /// /// 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); - /// /// 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 = options; - _isService = isService; - } - /// - /// Appends the copy right header to the source builder. + /// Gets or sets the set of methods used by the source builder. /// - /// The updated source builder. - internal SourceBuilder AppendCopyRightHeader() - { - _builder.Append(""" - // Copyright (c) David Pine. All rights reserved. - // Licensed under the MIT License: - // https://bit.ly/blazorators-license - // Auto-generated by blazorators. + internal ISet? Methods { get; private set; } + internal SourceBuilder AppendClosingCurlyBrace(bool decreaseIndentation = false) + { + DecreaseIndentationImpl(decreaseIndentation); - """); + _builder.Append($"{_indentation}}}{NewLine}"); return this; } - internal SourceBuilder AppendUsingDeclarations() + internal SourceBuilder AppendConditionalDelegateCallbackMethods(List? methods) { - if (_options is { SupportsGenerics: true }) + if (methods is { Count: > 0 }) { - _builder.Append($"using Blazor.Serialization.Extensions;{NewLine}"); - _builder.Append($"using System.Text.Json;{NewLine}"); - } + var level = IndentationLevel; - if (!_options.IsWebAssembly) - { - _builder.Append($"using System.Threading.Tasks;{NewLine}"); - } + var callbacks = methods.SelectMany(m => m.ParameterDefinitions) + .Where(param => param.ActionDeclation is not null) + .GroupBy(param => param.RawName); - _builder.Append(NewLine); + foreach (var callback in callbacks) + { + var param = callback.First(); + var methodName = $"On{param.RawName.CapitalizeFirstLetter()}"; + Methods ??= new HashSet(); + if (Methods.Add(methodName)) + { + var fieldName = $"_{param.RawName}"; + + AppendRaw("[JSInvokable]"); + AppendRaw($"public void {methodName}(", appendNewLine: false); + AppendConditionalDelegateCallbackMethodParameters(param, fieldName); + + AppendLine(); + ResetIndentiationTo(level); + } + } + } return this; } - internal SourceBuilder AppendNamespace(string namespaceString, bool isNullableContext = true) + internal SourceBuilder AppendConditionalDelegateFields(List? methods) { - if (isNullableContext) + if (methods is { Count: > 0 }) { - _builder.Append($"#nullable enable{NewLine}"); - } + var callbacks = methods.SelectMany(m => m.ParameterDefinitions) + .Where(param => param.ActionDeclation is not null) + .GroupBy(param => param.RawName); - _builder.Append($"namespace {namespaceString};{_twoNewLines}"); + foreach (var callback in callbacks) + { + var param = callback.First(); + var keys = + param.ActionDeclation!.ParameterDefinitions.Select(p => p.RawTypeName); + + var fieldName = $"_{param.RawName}"; + Fields ??= new HashSet(); + Fields.Add(fieldName); + + AppendRaw($"private Action<{string.Join(", ", keys)}>? {fieldName};"); + } + + AppendLine(); + } return this; } - internal SourceBuilder AppendPublicInterfaceDeclaration() + /// + /// Appends the copy right header to the source builder. + /// + /// The updated source builder. + internal SourceBuilder AppendCopyRightHeader() { - _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(""" + // Copyright (c) David Pine. All rights reserved. + // Licensed under the MIT License: + // https://bit.ly/blazorators-license + // Auto-generated by blazorators. + + """); return this; } - internal SourceBuilder AppendInternalImplementationDeclaration() + internal SourceBuilder AppendEmptyTripleSlashInheritdocComments(IndentationAdjustment adjustment = IndentationAdjustment.NoOp) { - _builder.Append($"/// {NewLine}"); - _builder.Append($"internal sealed class {ImplementationName} : {InterfaceName}{NewLine}"); + AdjustIndentation(adjustment); + var indent = _indentation.ToString(); + + _builder.Append($"{indent}/// {NewLine}"); return this; } @@ -139,6 +167,36 @@ internal SourceBuilder AppendImplementationCtor() return this; } + internal SourceBuilder AppendInternalImplementationDeclaration() + { + _builder.Append($"/// {NewLine}"); + _builder.Append($"internal sealed class {ImplementationName} : {InterfaceName}{NewLine}"); + + return this; + } + + 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); + + return this; + } + + internal SourceBuilder AppendNamespace(string namespaceString, bool isNullableContext = true) + { + if (isNullableContext) + { + _builder.Append($"#nullable enable{NewLine}"); + } + + _builder.Append($"namespace {namespaceString};{_twoNewLines}"); + + return this; + } + internal SourceBuilder AppendOpeningCurlyBrace(bool increaseIndentation = false) { IncreaseIndentationImpl(increaseIndentation); @@ -148,19 +206,40 @@ internal SourceBuilder AppendOpeningCurlyBrace(bool increaseIndentation = false) return this; } - internal SourceBuilder AppendClosingCurlyBrace(bool decreaseIndentation = false) + internal SourceBuilder AppendPublicInterfaceDeclaration() { - DecreaseIndentationImpl(decreaseIndentation); + _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($"{_indentation}}}{NewLine}"); + return this; + } + + internal SourceBuilder AppendRaw(string content, bool appendNewLine = true, bool postIncreaseIndentation = false, bool omitIndentation = false) + { + var indentation = omitIndentation ? "" : _indentation.ToString(); + _builder.Append($"{indentation}{content}{(appendNewLine ? NewLine : string.Empty)}"); + + if (postIncreaseIndentation) + { + IncreaseIndentation(); + } return this; } - internal SourceBuilder AppendTripleSlashMethodComments( - CSharpMethod method, - bool extrapolateParameters = false, - IndentationAdjustment adjustment = IndentationAdjustment.NoOp) + internal SourceBuilder AppendTripleSlashInheritdocComments(string csharpTypeName, string memberName, IndentationAdjustment adjustment = IndentationAdjustment.NoOp) + { + AdjustIndentation(adjustment); + var indent = _indentation.ToString(); + + _builder.Append($"{indent}/// {NewLine}"); + + return this; + } + + internal SourceBuilder AppendTripleSlashMethodComments(CSharpMethod method, bool extrapolateParameters = false, IndentationAdjustment adjustment = IndentationAdjustment.NoOp) { AdjustIndentation(adjustment); var indent = _indentation.ToString(); @@ -181,8 +260,7 @@ internal SourceBuilder AppendTripleSlashMethodComments( { if (index.IsFirst) { - _builder.Append( - $"/// The calling Razor (or Blazor) component.{NewLine}"); + _builder.Append($"{indent}/// The calling Razor (or Blazor) component.{NewLine}"); } if (param.ActionDeclation is not null) @@ -190,15 +268,14 @@ internal SourceBuilder AppendTripleSlashMethodComments( var name = param.ToArgumentString(); var dependentTypes = param.ActionDeclation.DependentTypes.Keys; var action = - $"Expects the name of a \"JSInvokableAttribute\" C# method with the following " + + "Expects the name of a \"JSInvokableAttribute\" C# method with the following " + $"System.Action{{{string.Join(", ", dependentTypes)}}}\"."; - _builder.Append( - $"/// {action}{NewLine}"); + + _builder.Append($"{indent}/// {action}{NewLine}"); } else { - _builder.Append( - $"/// The {param.RawTypeName} value.{NewLine}"); + _builder.Append($"{indent}/// The {param.RawTypeName} value.{NewLine}"); } } } @@ -206,33 +283,7 @@ internal SourceBuilder AppendTripleSlashMethodComments( return this; } - internal SourceBuilder AppendEmptyTripleSlashInheritdocComments( - IndentationAdjustment adjustment = IndentationAdjustment.NoOp) - { - AdjustIndentation(adjustment); - var indent = _indentation.ToString(); - - _builder.Append($"{indent}/// {NewLine}"); - - return this; - } - - internal SourceBuilder AppendTripleSlashInheritdocComments( - string csharpTypeName, - string memberName, - IndentationAdjustment adjustment = IndentationAdjustment.NoOp) - { - AdjustIndentation(adjustment); - var indent = _indentation.ToString(); - - _builder.Append($"{indent}/// {NewLine}"); - - return this; - } - - internal SourceBuilder AppendTripleSlashPropertyComments( - CSharpProperty property, - IndentationAdjustment adjustment = IndentationAdjustment.NoOp) + internal SourceBuilder AppendTripleSlashPropertyComments(CSharpProperty property, IndentationAdjustment adjustment = IndentationAdjustment.NoOp) { AdjustIndentation(adjustment); var indent = _indentation.ToString(); @@ -250,29 +301,28 @@ internal SourceBuilder AppendTripleSlashPropertyComments( return this; } - internal SourceBuilder AppendLine() + internal SourceBuilder AppendUsingDeclarations() { - // 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. + if (_options is { SupportsGenerics: true }) + { + _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($"#nullable enable{NewLine}"); _builder.Append(NewLine); return this; } - internal SourceBuilder AppendRaw( - string content, - bool appendNewLine = true, - bool postIncreaseIndentation = false, - bool omitIndentation = false) + internal SourceBuilder DecreaseIndentation() { - var indentation = omitIndentation ? "" : _indentation.ToString(); - _builder.Append($"{indentation}{content}{(appendNewLine ? NewLine : string.Empty)}"); - - if (postIncreaseIndentation) - { - IncreaseIndentation(); - } + DecreaseIndentationImpl(true); return this; } @@ -284,112 +334,48 @@ internal SourceBuilder IncreaseIndentation() return this; } - internal SourceBuilder AppendConditionalDelegateFields(List? methods) + internal SourceBuilder ResetIndentiationTo(int level) { - if (methods is { Count: > 0 }) - { - foreach (var group in - methods.SelectMany(m => m.ParameterDefinitions) - .Where(param => param.ActionDeclation is not null) - .GroupBy(param => param.RawName)) - { - var param = group.First(); - var keys = - param.ActionDeclation!.ParameterDefinitions.Select(p => p.RawTypeName); - - var fieldName = $"_{param.RawName}"; - Fields ??= new HashSet(); - Fields.Add(fieldName); - - AppendRaw($"private Action<{string.Join(", ", keys)}>? {fieldName};"); - } - - AppendLine(); - } + _indentation = _indentation.ResetTo(level); return this; } - internal SourceBuilder AppendConditionalDelegateCallbackMethods(List? methods) - { - if (methods is { Count: > 0 }) - { - var level = IndentationLevel; - - foreach (var group in - methods.SelectMany(m => m.ParameterDefinitions) - .Where(param => param.ActionDeclation is not null) - .GroupBy(param => param.RawName)) - { - var param = group.First(); - var methodName = $"On{param.RawName.CapitalizeFirstLetter()}"; - Methods ??= new HashSet(); - if (Methods.Add(methodName)) - { - var fieldName = $"_{param.RawName}"; - - AppendRaw("[JSInvokable]"); - AppendRaw($"public void {methodName}(", postIncreaseIndentation: true); - AppendParameters(param, fieldName); - } - } - - AppendLine(); - ResetIndentiationTo(level); - } + internal string ToSourceCodeString() => _builder.ToString(); - return this; - } + private void AdjustIndentation(IndentationAdjustment adjustment) => _indentation = adjustment switch + { + IndentationAdjustment.Increase => _indentation.Increase(), + IndentationAdjustment.Decrease => _indentation.Decrease(), + _ => _indentation + }; - private void AppendParameters(CSharpType param, string fieldName) + private void AppendConditionalDelegateCallbackMethodParameters(CSharpType param, string fieldName) { var args = new List(); foreach (var (interation, p) in param.ActionDeclation!.ParameterDefinitions!.Select()) { args.Add(p.RawName); - AppendRaw($"{p.RawTypeName} {p.RawName}", appendNewLine: false); + AppendRaw($"{p.RawTypeName} {p.RawName}", appendNewLine: false, omitIndentation: true); if (interation.HasMore) { AppendRaw(","); } else { - AppendRaw(") =>", postIncreaseIndentation: true); + AppendRaw(") =>", postIncreaseIndentation: true, omitIndentation: true); AppendRaw($"{fieldName}?.Invoke({string.Join(", ", args)});"); } } } - internal SourceBuilder DecreaseIndentation() - { - DecreaseIndentationImpl(true); - - return this; - } - - internal SourceBuilder ResetIndentiationTo(int level) - { - _indentation = _indentation.ResetTo(level); - - return this; - } - - private void IncreaseIndentationImpl(bool increaseIndentation = false) => - AdjustIndentation(increaseIndentation - ? IndentationAdjustment.Increase - : IndentationAdjustment.NoOp); - private void DecreaseIndentationImpl(bool decreaseIndentation = false) => AdjustIndentation(decreaseIndentation ? IndentationAdjustment.Decrease : IndentationAdjustment.NoOp); - private void AdjustIndentation(IndentationAdjustment adjustment) => _indentation = adjustment switch - { - IndentationAdjustment.Increase => _indentation.Increase(), - IndentationAdjustment.Decrease => _indentation.Decrease(), - _ => _indentation - }; - - internal string ToSourceCodeString() => _builder.ToString(); -} + private void IncreaseIndentationImpl(bool increaseIndentation = false) => + AdjustIndentation(increaseIndentation + ? IndentationAdjustment.Increase + : IndentationAdjustment.NoOp); +} \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpAction.cs b/src/Blazor.SourceGenerators/CSharp/CSharpAction.cs index c0dd5e2a..407b0275 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpAction.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpAction.cs @@ -37,12 +37,4 @@ internal record CSharpAction( .Select(kvp => new DependentType(kvp.Key, kvp.Value)) .Concat(ParameterDefinitions?.SelectMany(parameter => parameter.AllDependentTypes) ?? []) .ToImmutableHashSet(DependentTypeComparer.Default); - - /// - /// Adds a dependent type to the collection. - /// - /// The name of the type. - /// The C# object representing the type. - public void AddDependentType(string typeName, CSharpObject csharpObject) => - DependentTypes[typeName] = csharpObject; } \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs index 62681968..ec1006c2 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs @@ -57,22 +57,28 @@ internal string ToInterfaceString(GeneratorOptions options, string? namespaceStr if (isPureNonBiDirectionalOrOverriddenJS) { + var hasParameters = method.ParameterDefinitions.Count > 0; + builder.AppendTripleSlashMethodComments(details.Method) - .AppendRaw($"{details.ReturnType} {details.CSharpMethodName}{details.Suffix}{details.GenericTypeArgs}(", appendNewLine: false, postIncreaseIndentation: true); + .AppendRaw($"{details.ReturnType} {details.CSharpMethodName}{details.Suffix}{details.GenericTypeArgs}(", + appendNewLine: hasParameters, + postIncreaseIndentation: hasParameters); - if (method.ParameterDefinitions.Count > 0) + if (hasParameters) { - AppendMethodParameters(builder, method, details, options); + AppendMethodParameters(builder, method, details, options, suffix: ");"); } else { - builder.AppendRaw(");", appendNewLine: true, omitIndentation: true); + builder.AppendRaw(");", omitIndentation: true); } } else if (!options.OnlyGeneratePureJS) { AppendNonPureMethod(builder, method, details, options); } + + builder.AppendLine(); } // Properties @@ -90,37 +96,32 @@ internal string ToInterfaceString(GeneratorOptions options, string? namespaceStr return TryFormatCSharpSourceText(builder.ToSourceCodeString()); } - private static void AppendMethodParameters(SourceBuilder builder, CSharpMethod method, MethodBuilderDetails details, GeneratorOptions options) + private static void AppendMethodParameters(SourceBuilder builder, CSharpMethod method, MethodBuilderDetails details, GeneratorOptions options, string suffix = "", bool asDelegate = false) { foreach (var (pi, parameter) in method.ParameterDefinitions.Select()) { var isGenericType = parameter.IsGenericParameter(method.RawName, options); + var parameterString = asDelegate + ? parameter.ToActionString(isGenericType) + : parameter.ToParameterString(isGenericType); + if (pi.IsLast) { if (details.IsSerializable) { - builder.AppendRaw($"{parameter.ToParameterString(isGenericType)},") - .AppendRaw($"JsonSerializerOptions? options = null);") - .AppendLine(); + builder.AppendRaw($"{parameterString},") + .AppendRaw($"JsonSerializerOptions? options = null{suffix}", appendNewLine: false); } else { - builder.AppendRaw($"{parameter.ToParameterString(isGenericType)});") - .AppendLine(); + builder.AppendRaw($"{parameterString}{suffix}", appendNewLine: false); } } else { - if (pi.IsFirst) - { - builder.AppendLine(); - } - - builder.AppendRaw($"{parameter.ToParameterString(isGenericType)},"); + builder.AppendRaw($"{parameterString},"); } } - - builder.DecreaseIndentation(); } private static void AppendNonPureMethod(SourceBuilder builder, CSharpMethod method, MethodBuilderDetails details, GeneratorOptions options) @@ -128,12 +129,12 @@ private static void AppendNonPureMethod(SourceBuilder builder, CSharpMethod meth var genericTypeArgs = details.GenericTypeArgs ?? MethodBuilderDetails.ToGenericTypeArgument(MethodBuilderDetails.GenericComponentType); builder.AppendTripleSlashMethodComments(details.Method, extrapolateParameters: true) - .AppendRaw($"{details.ReturnType} {details.CSharpMethodName}{details.Suffix}{genericTypeArgs}(") - .AppendRaw("TComponent component", appendNewLine: false, postIncreaseIndentation: true); + .AppendRaw($"{details.ReturnType} {details.CSharpMethodName}{details.Suffix}{genericTypeArgs}(", postIncreaseIndentation: true) + .AppendRaw("TComponent component", appendNewLine: false); if (method.ParameterDefinitions.Count > 0) { - builder.AppendRaw(","); + builder.AppendRaw(",", omitIndentation: true); foreach (var (pi, parameter) in method.ParameterDefinitions.Select()) { var isGenericType = parameter.IsGenericParameter(method.RawName, options); @@ -144,11 +145,6 @@ private static void AppendNonPureMethod(SourceBuilder builder, CSharpMethod meth } else { - if (pi.IsFirst) - { - builder.AppendLine(); - } - builder.AppendRaw($"{parameter.ToParameterString(isGenericType)},"); } } @@ -175,11 +171,6 @@ private static void AppendNonPureMethod(SourceBuilder builder, CSharpMethod meth } else { - if (pi.IsFirst) - { - builder.AppendLine(); - } - builder.AppendRaw($"{parameter.ToActionString(isGenericType)},"); } } @@ -188,7 +179,7 @@ private static void AppendNonPureMethod(SourceBuilder builder, CSharpMethod meth } else { - builder.AppendRaw(");", appendNewLine: true, omitIndentation: true); + builder.AppendRaw(");", omitIndentation: true); } } @@ -223,11 +214,11 @@ internal string ToImplementationString(GeneratorOptions options, string? namespa } // Properties - foreach (var property in Properties) + foreach (var (index, property) in Properties.Select()) { if (!property.IsIndexer) { - AppendImplementationProperty(builder, property, options); + AppendImplementationProperty(builder, property, options, methodLevel, index); } } @@ -244,9 +235,8 @@ internal static string ToServiceCollectionExtensions(GeneratorOptions options, s : "Scoped"; var addExpression = options.IsWebAssembly ? $$""" - services.Add{{serviceLifetime}}(serviceProvider => - (IJSInProcessRuntime)serviceProvider.GetRequiredService()) - + services.Add{{serviceLifetime}}(serviceProvider => + (IJSInProcessRuntime)serviceProvider.GetRequiredService()) """ : "services"; @@ -296,39 +286,112 @@ private static void AppendImplementationMethod(SourceBuilder builder, CSharpMeth else if (!options.OnlyGeneratePureJS) { AppendNonPureMethodImplementation(builder, method, details, options); + + builder.AppendLine(); + + AppendActionCallbackMethodImplementation(builder, method, details, options); } + + builder.AppendLine(); } - private static void AppendPureMethod(SourceBuilder builder, CSharpMethod method, MethodBuilderDetails details, GeneratorOptions options) + private static void AppendActionCallbackMethodImplementation(SourceBuilder builder, CSharpMethod method, MethodBuilderDetails details, GeneratorOptions options) { var memberName = $"{details.CSharpMethodName}{details.Suffix}"; + builder.AppendTripleSlashInheritdocComments(builder.InterfaceName, memberName) - .AppendRaw($"{details.ReturnType} {builder.InterfaceName}.{details.CSharpMethodName}{details.Suffix}{details.GenericTypeArgs}(", appendNewLine: false, postIncreaseIndentation: true); + .AppendRaw($"{details.ReturnType} {builder.InterfaceName}.{details.CSharpMethodName}{details.Suffix}(", + postIncreaseIndentation: true); if (method.ParameterDefinitions.Count > 0) { + AppendMethodParameters(builder, method, details, options, suffix: ") =>", asDelegate: true); + } + else + { + builder.AppendRaw(") =>", appendNewLine: true, omitIndentation: true); + } + + builder.AppendLine(); + + if (details.IsVoid) + { + builder.AppendRaw($"_javaScript.InvokeVoid{details.Suffix}("); + } + else + { + builder.AppendRaw($"_javaScript.Invoke{details.Suffix}<{details.BareType}>("); + } + + builder.IncreaseIndentation() + .AppendRaw($"\"{details.FullyQualifiedJavaScriptIdentifier}\","); + + AppendActionJavascriptParameters(method, builder, options); + + builder.DecreaseIndentation(); + } + + private static void AppendActionJavascriptParameters(CSharpMethod method, SourceBuilder builder, GeneratorOptions options) + { + foreach (var (pi, parameter) in method.ParameterDefinitions.Select()) + { + if (pi.IsFirst) + { + builder.AppendRaw($"DotNetObjectReference.Create(this),"); + } + + var isGenericType = parameter.IsGenericParameter(method.RawName, options); + var argument = parameter.ToArgumentString(isGenericType, asDelegate: true); + var methodName = builder.Methods?.FirstOrDefault(method => method.EndsWith(argument.Substring(2))); + var argExpression = methodName is not null ? $"nameof({methodName})" : argument; + + if (pi.IsLast) + { + builder.AppendRaw($"{argExpression});"); + } + else + { + builder.AppendRaw($"{argExpression},"); + } + } + + builder.DecreaseIndentation(); + } + + private static void AppendPureMethod(SourceBuilder builder, CSharpMethod method, MethodBuilderDetails details, GeneratorOptions options) + { + var memberName = $"{details.CSharpMethodName}{details.Suffix}"; + var hasParameters = method.ParameterDefinitions.Count > 0; + + builder.AppendTripleSlashInheritdocComments(builder.InterfaceName, memberName) + .AppendRaw($"{details.ReturnType} {builder.InterfaceName}.{details.CSharpMethodName}{details.Suffix}{details.GenericTypeArgs}(", + appendNewLine: hasParameters, + postIncreaseIndentation: hasParameters); + + if (hasParameters) + { + var genericTypeParameterConstraint = details.IsGenericReturnType + ? $" where {MethodBuilderDetails.GenericTypeValue} : default" + : ""; + foreach (var (pi, parameter) in method.ParameterDefinitions.Select()) { var isGenericType = parameter.IsGenericParameter(method.RawName, options); if (pi.IsLast) { - builder.AppendRaw($"{parameter.ToParameterString(isGenericType)},") - .AppendRaw($"JsonSerializerOptions? options = null);") - .AppendLine() - .AppendOpeningCurlyBrace() - .IncreaseIndentation() - .AppendRaw("throw new NotImplementedException();") - .DecreaseIndentation() - .AppendClosingCurlyBrace(); + if (details.IsSerializable) + { + builder.AppendRaw($"{parameter.ToParameterString(isGenericType)},"); + builder.AppendRaw($"JsonSerializerOptions? options){genericTypeParameterConstraint} =>"); + } + else + { + builder.AppendRaw($"{parameter.ToParameterString(isGenericType: false, overrideNullability: true)}){genericTypeParameterConstraint} =>"); + } } else { - if (pi.IsFirst) - { - builder.AppendLine(); - } - - builder.AppendRaw($"{parameter.ToParameterString(isGenericType)},"); + builder.AppendRaw($"{parameter.ToParameterString(isGenericType, overrideNullability: true)},"); } } @@ -336,13 +399,60 @@ private static void AppendPureMethod(SourceBuilder builder, CSharpMethod method, } else { - builder.AppendRaw(");") - .AppendLine() - .AppendOpeningCurlyBrace() - .IncreaseIndentation() - .AppendRaw("throw new NotImplementedException();") - .DecreaseIndentation() - .AppendClosingCurlyBrace(); + builder.AppendRaw(") =>", omitIndentation: true); + } + + builder.IncreaseIndentation(); + + if (details.IsVoid) + { + builder.AppendRaw($"_javaScript.InvokeVoid{details.Suffix}("); + } + else + { + builder.AppendRaw($"_javaScript.Invoke{details.Suffix}<{details.BareType}>("); + } + + builder.IncreaseIndentation() + .AppendRaw($"\"{details.FullyQualifiedJavaScriptIdentifier}\"", appendNewLine: false); + + if (hasParameters) + { + AppendPureJavascriptParameters(method, builder, details, options); + } + else + { + builder.AppendRaw(");", omitIndentation: true); + } + + builder.DecreaseIndentation(); + builder.DecreaseIndentation(); + } + + private static void AppendPureJavascriptParameters(CSharpMethod method, SourceBuilder builder, MethodBuilderDetails details, GeneratorOptions options) + { + builder.AppendRaw(",", omitIndentation: true); + + foreach (var (ai, parameter) in method.ParameterDefinitions.Select()) + { + var isGenericType = parameter.IsGenericParameter(method.RawName, options); + if (ai.IsLast) + { + if (details.IsGenericReturnType) + { + // Overridden to control explicitly + builder.AppendRaw($"{parameter.ToArgumentString(toJson: false)})"); + builder.AppendRaw($".FromJson{details.GenericTypeArgs}(options);"); + } + else + { + builder.AppendRaw($"{parameter.ToArgumentString(details.ContainsGenericParameters)});"); + } + } + else + { + builder.AppendRaw($"{parameter.ToArgumentString(isGenericType)},"); + } } } @@ -352,35 +462,76 @@ private static void AppendNonPureMethodImplementation(SourceBuilder builder, CSh var memberName = $"{details.CSharpMethodName}{details.Suffix}"; builder.AppendTripleSlashInheritdocComments(builder.InterfaceName, memberName) - .AppendRaw($"{details.ReturnType} {builder.InterfaceName}.{details.CSharpMethodName}{details.Suffix}{genericTypeArgs}(", postIncreaseIndentation: true); + .AppendRaw($"{details.ReturnType} {builder.InterfaceName}.{details.CSharpMethodName}{details.Suffix}{genericTypeArgs}(", postIncreaseIndentation: true) + .AppendRaw("TComponent component", appendNewLine: false); if (method.ParameterDefinitions.Count > 0) { - AppendMethodParameters(builder, method, details, options); + builder.AppendRaw(",", omitIndentation: true); + AppendMethodParameters(builder, method, details, options, suffix: ") where TComponent : class =>"); } else { - builder.AppendRaw(") where TComponent : class;", appendNewLine: true, omitIndentation: true); + builder.AppendRaw(") where TComponent : class =>", appendNewLine: true, omitIndentation: true); + } + + builder.AppendLine(); + + if (details.IsVoid) + { + builder.AppendRaw($"_javaScript.InvokeVoid{details.Suffix}("); + } + else + { + builder.AppendRaw($"_javaScript.Invoke{details.Suffix}<{details.BareType}>("); } - builder.AppendRaw("throw new NotImplementedException();") - .DecreaseIndentation() - .AppendClosingCurlyBrace(); + builder.IncreaseIndentation() + .AppendRaw($"\"{details.FullyQualifiedJavaScriptIdentifier}\","); + + builder.AppendRaw($"DotNetObjectReference.Create(component),"); + + AppendNonPureJavascriptParameters(method, builder, options); + + builder.DecreaseIndentation(); } - private static void AppendImplementationProperty(SourceBuilder builder, CSharpProperty property, GeneratorOptions options) + private static void AppendNonPureJavascriptParameters(CSharpMethod method, SourceBuilder builder, GeneratorOptions options) { + foreach (var (ai, parameter) in method.ParameterDefinitions.Select()) + { + var isGenericType = parameter.IsGenericParameter(method.RawName, options); + if (ai.IsLast) + { + builder.AppendRaw($"{parameter.ToArgumentString(isGenericType)});"); + } + else + { + builder.AppendRaw($"{parameter.ToArgumentString(isGenericType)},"); + } + } + + builder.DecreaseIndentation(); + } + + private static void AppendImplementationProperty(SourceBuilder builder, CSharpProperty property, GeneratorOptions options, int methodLevel, Iteration index) + { + if (index.IsFirst) builder.AppendLine(); + if (property.IsIndexer) return; + + builder.ResetIndentiationTo(methodLevel); + var details = PropertyBuilderDetails.Create(property, options); - var accessors = details.Property.IsReadonly ? "{ get; }" : "{ get; set; }"; - var memberName = $"{details.CSharpPropertyName}"; - builder.AppendTripleSlashInheritdocComments(builder.InterfaceName, memberName) - .AppendRaw($"{details.ReturnType} {builder.InterfaceName}.{details.CSharpPropertyName} {accessors}") - .AppendLine() - .AppendOpeningCurlyBrace() - .IncreaseIndentation() - .AppendRaw("throw new NotImplementedException();") - .DecreaseIndentation() - .AppendClosingCurlyBrace(); + + builder.AppendTripleSlashInheritdocComments(builder.InterfaceName, details.CSharpPropertyName) + .AppendRaw($"{details.ReturnType} {builder.InterfaceName}.{details.CSharpPropertyName} =>", postIncreaseIndentation: true) + .AppendRaw($"_javaScript.Invoke{details.Suffix}{details.GenericTypeArgs}(", postIncreaseIndentation: true) + .AppendRaw($"\"eval\", \"{details.FullyQualifiedJavaScriptIdentifier}\");"); + + if (!index.IsLast) + { + builder.AppendLine(); + } } /// From a46b6dc9b0557ed0edf532108918f08c7e6fda82 Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Wed, 17 Jul 2024 22:06:50 +0200 Subject: [PATCH 29/41] chore: for now removed speech synthesis causing error. Looking for fix --- .../Blazor.ExampleConsumer.csproj | 2 +- .../Components/Pages/ReadToMe.razor | 4 +- .../Components/Pages/ReadToMe.razor.cs | 198 +++++++++--------- samples/Blazor.ExampleConsumer/Program.cs | 4 +- .../BlazorServer.ExampleConsumer.csproj | 2 +- .../Pages/ReadToMe.razor | 4 +- .../Pages/ReadToMe.razor.cs | 184 ++++++++-------- .../BlazorServer.ExampleConsumer/Program.cs | 4 +- 8 files changed, 201 insertions(+), 201 deletions(-) diff --git a/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj b/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj index 557a0956..f5770adf 100644 --- a/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj +++ b/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj @@ -18,7 +18,7 @@ - + diff --git a/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor b/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor index b96d0ae3..991e19c4 100644 --- a/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor +++ b/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor @@ -1,4 +1,4 @@ -@page "/speak" +@* @page "/speak" Text-to-speech @@ -55,4 +55,4 @@ - \ No newline at end of file + *@ \ No newline at end of file diff --git a/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor.cs b/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor.cs index ae8e1073..dcddfa40 100644 --- a/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor.cs +++ b/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor.cs @@ -1,99 +1,99 @@ -// Copyright (c) David Pine. All rights reserved. -// Licensed under the MIT License. - -using Humanizer; - -namespace Blazor.ExampleConsumer.Components.Pages; - -public sealed partial class ReadToMe : IDisposable -{ - const string PreferredVoiceKey = "preferred-voice"; - const string PreferredSpeedKey = "preferred-speed"; - const string TextKey = "read-to-me-text"; - - string? _text = "Blazorators is an open-source project that strives to simplify JavaScript interop in Blazor. JavaScript interoperability is possible by parsing TypeScript type declarations and using this metadata to output corresponding C# types."; - SpeechSynthesisVoice[] _voices = Array.Empty(); - readonly IList _voiceSpeeds = - Enumerable.Range(0, 12).Select(i => (i + 1) * .25).ToList(); - double _voiceSpeed = 1.5; - string? _selectedVoice; - string? _elapsedTimeMessage = null; - - SpeechSynthesisUtterance Utterance => new() - { - Text = _text ?? "You forgot to try uttering some text.", - Rate = _voiceSpeed, - Volume = 1, - Voice = _selectedVoice is { Length: > 0 } - ? _voices?.FirstOrDefault(voice => voice.Name == _selectedVoice) - : null - }; - - [Inject] - public ISpeechSynthesisService SpeechSynthesis { get; set; } = null!; - - [Inject] - public ILocalStorageService LocalStorage { get; set; } = null!; - - [Inject] - public ISessionStorageService SessionStorage { get; set; } = null!; - - [Inject] - public ILogger Logger { get; set; } = null!; - - protected override async Task OnInitializedAsync() - { - await GetVoicesAsync(); - SpeechSynthesis.OnVoicesChanged(() => GetVoicesAsync(true)); - - if (LocalStorage.GetItem(PreferredVoiceKey) - is { Length: > 0 } voice) - { - _selectedVoice = voice; - } - if (LocalStorage.GetItem(PreferredSpeedKey) - is double speed && speed > 0) - { - _voiceSpeed = speed; - } - if (SessionStorage.GetItem(TextKey) - is { Length: > 0 } text) - { - _text = text; - } - } - - async Task GetVoicesAsync(bool isFromCallback = false) - { - _voices = await SpeechSynthesis.GetVoicesAsync(); - if (_voices is { } && isFromCallback) - { - StateHasChanged(); - } - } - - void OnTextChanged(ChangeEventArgs args) => _text = args.Value?.ToString(); - - void OnVoiceSpeedChange(ChangeEventArgs args) => - _voiceSpeed = double.TryParse(args.Value?.ToString() ?? "1.5", out var speed) - ? speed : 1.5; - - void Speak() => SpeechSynthesis.Speak( - Utterance, - elapsedTime => - { - _elapsedTimeMessage = - $"Read aloud in {TimeSpan.FromMilliseconds(elapsedTime).Humanize()}."; - - StateHasChanged(); - }); - - void IDisposable.Dispose() - { - LocalStorage.SetItem(PreferredVoiceKey, _selectedVoice); - LocalStorage.SetItem(PreferredSpeedKey, _voiceSpeed); - SessionStorage.SetItem(TextKey, _text); - - SpeechSynthesis.UnsubscribeFromVoicesChanged(); - } -} +//// Copyright (c) David Pine. All rights reserved. +//// Licensed under the MIT License. + +//using Humanizer; + +//namespace Blazor.ExampleConsumer.Components.Pages; + +//public sealed partial class ReadToMe : IDisposable +//{ +// const string PreferredVoiceKey = "preferred-voice"; +// const string PreferredSpeedKey = "preferred-speed"; +// const string TextKey = "read-to-me-text"; + +// string? _text = "Blazorators is an open-source project that strives to simplify JavaScript interop in Blazor. JavaScript interoperability is possible by parsing TypeScript type declarations and using this metadata to output corresponding C# types."; +// SpeechSynthesisVoice[] _voices = Array.Empty(); +// readonly IList _voiceSpeeds = +// Enumerable.Range(0, 12).Select(i => (i + 1) * .25).ToList(); +// double _voiceSpeed = 1.5; +// string? _selectedVoice; +// string? _elapsedTimeMessage = null; + +// SpeechSynthesisUtterance Utterance => new() +// { +// Text = _text ?? "You forgot to try uttering some text.", +// Rate = _voiceSpeed, +// Volume = 1, +// Voice = _selectedVoice is { Length: > 0 } +// ? _voices?.FirstOrDefault(voice => voice.Name == _selectedVoice) +// : null +// }; + +// [Inject] +// public ISpeechSynthesisService SpeechSynthesis { get; set; } = null!; + +// [Inject] +// public ILocalStorageService LocalStorage { get; set; } = null!; + +// [Inject] +// public ISessionStorageService SessionStorage { get; set; } = null!; + +// [Inject] +// public ILogger Logger { get; set; } = null!; + +// protected override async Task OnInitializedAsync() +// { +// await GetVoicesAsync(); +// SpeechSynthesis.OnVoicesChanged(() => GetVoicesAsync(true)); + +// if (LocalStorage.GetItem(PreferredVoiceKey) +// is { Length: > 0 } voice) +// { +// _selectedVoice = voice; +// } +// if (LocalStorage.GetItem(PreferredSpeedKey) +// is double speed && speed > 0) +// { +// _voiceSpeed = speed; +// } +// if (SessionStorage.GetItem(TextKey) +// is { Length: > 0 } text) +// { +// _text = text; +// } +// } + +// async Task GetVoicesAsync(bool isFromCallback = false) +// { +// _voices = await SpeechSynthesis.GetVoicesAsync(); +// if (_voices is { } && isFromCallback) +// { +// StateHasChanged(); +// } +// } + +// void OnTextChanged(ChangeEventArgs args) => _text = args.Value?.ToString(); + +// void OnVoiceSpeedChange(ChangeEventArgs args) => +// _voiceSpeed = double.TryParse(args.Value?.ToString() ?? "1.5", out var speed) +// ? speed : 1.5; + +// void Speak() => SpeechSynthesis.Speak( +// Utterance, +// elapsedTime => +// { +// _elapsedTimeMessage = +// $"Read aloud in {TimeSpan.FromMilliseconds(elapsedTime).Humanize()}."; + +// StateHasChanged(); +// }); + +// void IDisposable.Dispose() +// { +// LocalStorage.SetItem(PreferredVoiceKey, _selectedVoice); +// LocalStorage.SetItem(PreferredSpeedKey, _voiceSpeed); +// SessionStorage.SetItem(TextKey, _text); + +// SpeechSynthesis.UnsubscribeFromVoicesChanged(); +// } +//} \ No newline at end of file diff --git a/samples/Blazor.ExampleConsumer/Program.cs b/samples/Blazor.ExampleConsumer/Program.cs index e1e070fa..179015e9 100644 --- a/samples/Blazor.ExampleConsumer/Program.cs +++ b/samples/Blazor.ExampleConsumer/Program.cs @@ -18,9 +18,9 @@ builder.Services.AddLocalStorageServices(); builder.Services.AddSessionStorageServices(); builder.Services.AddGeolocationServices(); -builder.Services.AddSpeechSynthesisServices(); +//builder.Services.AddSpeechSynthesisServices(); // Custom library bits... builder.Services.AddSpeechRecognitionServices(); -await builder.Build().RunAsync(); +await builder.Build().RunAsync(); \ No newline at end of file diff --git a/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj b/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj index 138ed72f..0f272886 100644 --- a/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj +++ b/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj @@ -16,7 +16,7 @@ - + diff --git a/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor b/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor index be18f147..67844614 100644 --- a/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor +++ b/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor @@ -1,4 +1,4 @@ -@page "/speak" +@* @page "/speak" Text-to-speech @@ -46,4 +46,4 @@ Speak - \ No newline at end of file + *@ \ No newline at end of file diff --git a/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor.cs b/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor.cs index 4e5ff215..338b857f 100644 --- a/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor.cs +++ b/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor.cs @@ -1,92 +1,92 @@ -// Copyright (c) David Pine. All rights reserved. -// Licensed under the MIT License. - -namespace BlazorServer.ExampleConsumer.Pages; - -public sealed partial class ReadToMe : IAsyncDisposable -{ - const string PreferredVoiceKey = "preferred-voice"; - const string PreferredSpeedKey = "preferred-speed"; - const string TextKey = "read-to-me-text"; - - string? _text = "Blazorators is an open-source project that strives to simplify JavaScript interop in Blazor. JavaScript interoperability is possible by parsing TypeScript type declarations and using this metadata to output corresponding C# types."; - SpeechSynthesisVoice[] _voices = Array.Empty(); - readonly IList _voiceSpeeds = - Enumerable.Range(0, 12).Select(i => (i + 1) * .25).ToList(); - double _voiceSpeed = 1.5; - string? _selectedVoice; - string? _elapsedTimeMessage = null; - - SpeechSynthesisUtterance Utterance => new() - { - Text = _text ?? "You forgot to try uttering some text.", - Rate = _voiceSpeed, - Volume = 1, - Voice = _selectedVoice is { Length: > 0 } - ? _voices?.FirstOrDefault(voice => voice.Name == _selectedVoice) - : null - }; - - [Inject] - public ISpeechSynthesisService SpeechSynthesis { get; set; } = null!; - - [Inject] - public ILocalStorageService LocalStorage { get; set; } = null!; - - [Inject] - public ISessionStorageService SessionStorage { get; set; } = null!; - - [Inject] - public ILogger Logger { get; set; } = null!; - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender is false) - { - return; - } - - await GetVoicesAsync(); - - if (await LocalStorage.GetItemAsync(PreferredVoiceKey) - is { Length: > 0 } voice) - { - _selectedVoice = voice; - } - if (await LocalStorage.GetItemAsync(PreferredSpeedKey) - is { Length: > 0 } s && - double.TryParse(s, out var speed) && speed > 0) - { - _voiceSpeed = speed; - } - if (await SessionStorage.GetItemAsync(TextKey) - is { Length: > 0 } text) - { - _text = text; - } - } - - async Task GetVoicesAsync(bool isFromCallback = false) - { - _voices = await SpeechSynthesis.GetVoicesAsync(); - if (_voices is { } && isFromCallback) - { - StateHasChanged(); - } - } - - void OnTextChanged(ChangeEventArgs args) => _text = args.Value?.ToString(); - - void OnVoiceSpeedChange(ChangeEventArgs args) => - _voiceSpeed = double.TryParse(args.Value?.ToString() ?? "1.5", out var speed) - ? speed : 1.5; - - ValueTask Speak() => SpeechSynthesis.SpeakAsync(Utterance); - - async ValueTask IAsyncDisposable.DisposeAsync() - { - await LocalStorage.SetItemAsync(PreferredVoiceKey, _selectedVoice!); - await LocalStorage.SetItemAsync(PreferredSpeedKey, _voiceSpeed.ToString()); - await SessionStorage.SetItemAsync(TextKey, _text!); - } -} +//// Copyright (c) David Pine. All rights reserved. +//// Licensed under the MIT License. + +//namespace BlazorServer.ExampleConsumer.Pages; + +//public sealed partial class ReadToMe : IAsyncDisposable +//{ +// const string PreferredVoiceKey = "preferred-voice"; +// const string PreferredSpeedKey = "preferred-speed"; +// const string TextKey = "read-to-me-text"; + +// string? _text = "Blazorators is an open-source project that strives to simplify JavaScript interop in Blazor. JavaScript interoperability is possible by parsing TypeScript type declarations and using this metadata to output corresponding C# types."; +// SpeechSynthesisVoice[] _voices = Array.Empty(); +// readonly IList _voiceSpeeds = +// Enumerable.Range(0, 12).Select(i => (i + 1) * .25).ToList(); +// double _voiceSpeed = 1.5; +// string? _selectedVoice; +// string? _elapsedTimeMessage = null; + +// SpeechSynthesisUtterance Utterance => new() +// { +// Text = _text ?? "You forgot to try uttering some text.", +// Rate = _voiceSpeed, +// Volume = 1, +// Voice = _selectedVoice is { Length: > 0 } +// ? _voices?.FirstOrDefault(voice => voice.Name == _selectedVoice) +// : null +// }; + +// [Inject] +// public ISpeechSynthesisService SpeechSynthesis { get; set; } = null!; + +// [Inject] +// public ILocalStorageService LocalStorage { get; set; } = null!; + +// [Inject] +// public ISessionStorageService SessionStorage { get; set; } = null!; + +// [Inject] +// public ILogger Logger { get; set; } = null!; + +// protected override async Task OnAfterRenderAsync(bool firstRender) +// { +// if (firstRender is false) +// { +// return; +// } + +// await GetVoicesAsync(); + +// if (await LocalStorage.GetItemAsync(PreferredVoiceKey) +// is { Length: > 0 } voice) +// { +// _selectedVoice = voice; +// } +// if (await LocalStorage.GetItemAsync(PreferredSpeedKey) +// is { Length: > 0 } s && +// double.TryParse(s, out var speed) && speed > 0) +// { +// _voiceSpeed = speed; +// } +// if (await SessionStorage.GetItemAsync(TextKey) +// is { Length: > 0 } text) +// { +// _text = text; +// } +// } + +// async Task GetVoicesAsync(bool isFromCallback = false) +// { +// _voices = await SpeechSynthesis.GetVoicesAsync(); +// if (_voices is { } && isFromCallback) +// { +// StateHasChanged(); +// } +// } + +// void OnTextChanged(ChangeEventArgs args) => _text = args.Value?.ToString(); + +// void OnVoiceSpeedChange(ChangeEventArgs args) => +// _voiceSpeed = double.TryParse(args.Value?.ToString() ?? "1.5", out var speed) +// ? speed : 1.5; + +// ValueTask Speak() => SpeechSynthesis.SpeakAsync(Utterance); + +// async ValueTask IAsyncDisposable.DisposeAsync() +// { +// await LocalStorage.SetItemAsync(PreferredVoiceKey, _selectedVoice!); +// await LocalStorage.SetItemAsync(PreferredSpeedKey, _voiceSpeed.ToString()); +// await SessionStorage.SetItemAsync(TextKey, _text!); +// } +//} \ No newline at end of file diff --git a/samples/BlazorServer.ExampleConsumer/Program.cs b/samples/BlazorServer.ExampleConsumer/Program.cs index f5f6ee73..65c91aea 100644 --- a/samples/BlazorServer.ExampleConsumer/Program.cs +++ b/samples/BlazorServer.ExampleConsumer/Program.cs @@ -11,7 +11,7 @@ builder.Services.AddLocalStorageServices(); builder.Services.AddSessionStorageServices(); builder.Services.AddGeolocationServices(); -builder.Services.AddSpeechSynthesisServices(); +//builder.Services.AddSpeechSynthesisServices(); // Custom library bits... builder.Services.AddSpeechRecognitionServices(); @@ -32,4 +32,4 @@ app.MapBlazorHub(); app.MapFallbackToPage("/_Host"); -app.Run(); +app.Run(); \ No newline at end of file From 486a387a6234423f98d443110163f6638100ff73 Mon Sep 17 00:00:00 2001 From: "Koja sig. Dennis" Date: Thu, 18 Jul 2024 10:51:37 +0200 Subject: [PATCH 30/41] fix: nullable types not entirely recognized, speech syntheis works, all test passed --- .../Blazor.ExampleConsumer.csproj | 2 +- .../Components/Pages/ReadToMe.razor | 4 +- .../Components/Pages/ReadToMe.razor.cs | 198 +++++++++--------- samples/Blazor.ExampleConsumer/Program.cs | 2 +- .../BlazorServer.ExampleConsumer.csproj | 2 +- .../Pages/ReadToMe.razor | 4 +- .../Pages/ReadToMe.razor.cs | 184 ++++++++-------- .../BlazorServer.ExampleConsumer/Program.cs | 2 +- .../Builders/SourceBuilder.cs | 1 - .../CSharp/CSharpMethod.cs | 4 +- .../CSharp/CSharpTopLevelObject.cs | 2 + .../TypeDeclarationParser.Interfaces.cs | 51 ++++- .../Parsers/TypeDeclarationParser.cs | 4 +- 13 files changed, 248 insertions(+), 212 deletions(-) diff --git a/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj b/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj index f5770adf..557a0956 100644 --- a/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj +++ b/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj @@ -18,7 +18,7 @@ - + diff --git a/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor b/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor index 991e19c4..b96d0ae3 100644 --- a/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor +++ b/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor @@ -1,4 +1,4 @@ -@* @page "/speak" +@page "/speak" Text-to-speech @@ -55,4 +55,4 @@ - *@ \ No newline at end of file + \ No newline at end of file diff --git a/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor.cs b/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor.cs index dcddfa40..30599534 100644 --- a/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor.cs +++ b/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor.cs @@ -1,99 +1,99 @@ -//// Copyright (c) David Pine. All rights reserved. -//// Licensed under the MIT License. - -//using Humanizer; - -//namespace Blazor.ExampleConsumer.Components.Pages; - -//public sealed partial class ReadToMe : IDisposable -//{ -// const string PreferredVoiceKey = "preferred-voice"; -// const string PreferredSpeedKey = "preferred-speed"; -// const string TextKey = "read-to-me-text"; - -// string? _text = "Blazorators is an open-source project that strives to simplify JavaScript interop in Blazor. JavaScript interoperability is possible by parsing TypeScript type declarations and using this metadata to output corresponding C# types."; -// SpeechSynthesisVoice[] _voices = Array.Empty(); -// readonly IList _voiceSpeeds = -// Enumerable.Range(0, 12).Select(i => (i + 1) * .25).ToList(); -// double _voiceSpeed = 1.5; -// string? _selectedVoice; -// string? _elapsedTimeMessage = null; - -// SpeechSynthesisUtterance Utterance => new() -// { -// Text = _text ?? "You forgot to try uttering some text.", -// Rate = _voiceSpeed, -// Volume = 1, -// Voice = _selectedVoice is { Length: > 0 } -// ? _voices?.FirstOrDefault(voice => voice.Name == _selectedVoice) -// : null -// }; - -// [Inject] -// public ISpeechSynthesisService SpeechSynthesis { get; set; } = null!; - -// [Inject] -// public ILocalStorageService LocalStorage { get; set; } = null!; - -// [Inject] -// public ISessionStorageService SessionStorage { get; set; } = null!; - -// [Inject] -// public ILogger Logger { get; set; } = null!; - -// protected override async Task OnInitializedAsync() -// { -// await GetVoicesAsync(); -// SpeechSynthesis.OnVoicesChanged(() => GetVoicesAsync(true)); - -// if (LocalStorage.GetItem(PreferredVoiceKey) -// is { Length: > 0 } voice) -// { -// _selectedVoice = voice; -// } -// if (LocalStorage.GetItem(PreferredSpeedKey) -// is double speed && speed > 0) -// { -// _voiceSpeed = speed; -// } -// if (SessionStorage.GetItem(TextKey) -// is { Length: > 0 } text) -// { -// _text = text; -// } -// } - -// async Task GetVoicesAsync(bool isFromCallback = false) -// { -// _voices = await SpeechSynthesis.GetVoicesAsync(); -// if (_voices is { } && isFromCallback) -// { -// StateHasChanged(); -// } -// } - -// void OnTextChanged(ChangeEventArgs args) => _text = args.Value?.ToString(); - -// void OnVoiceSpeedChange(ChangeEventArgs args) => -// _voiceSpeed = double.TryParse(args.Value?.ToString() ?? "1.5", out var speed) -// ? speed : 1.5; - -// void Speak() => SpeechSynthesis.Speak( -// Utterance, -// elapsedTime => -// { -// _elapsedTimeMessage = -// $"Read aloud in {TimeSpan.FromMilliseconds(elapsedTime).Humanize()}."; - -// StateHasChanged(); -// }); - -// void IDisposable.Dispose() -// { -// LocalStorage.SetItem(PreferredVoiceKey, _selectedVoice); -// LocalStorage.SetItem(PreferredSpeedKey, _voiceSpeed); -// SessionStorage.SetItem(TextKey, _text); - -// SpeechSynthesis.UnsubscribeFromVoicesChanged(); -// } -//} \ No newline at end of file +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. + +using Humanizer; + +namespace Blazor.ExampleConsumer.Components.Pages; + +public sealed partial class ReadToMe : IDisposable +{ + const string PreferredVoiceKey = "preferred-voice"; + const string PreferredSpeedKey = "preferred-speed"; + const string TextKey = "read-to-me-text"; + + string? _text = "Blazorators is an open-source project that strives to simplify JavaScript interop in Blazor. JavaScript interoperability is possible by parsing TypeScript type declarations and using this metadata to output corresponding C# types."; + SpeechSynthesisVoice[] _voices = Array.Empty(); + readonly IList _voiceSpeeds = + Enumerable.Range(0, 12).Select(i => (i + 1) * .25).ToList(); + double _voiceSpeed = 1.5; + string? _selectedVoice; + string? _elapsedTimeMessage = null; + + SpeechSynthesisUtterance Utterance => new() + { + Text = _text ?? "You forgot to try uttering some text.", + Rate = _voiceSpeed, + Volume = 1, + Voice = _selectedVoice is { Length: > 0 } + ? _voices?.FirstOrDefault(voice => voice.Name == _selectedVoice) + : null + }; + + [Inject] + public ISpeechSynthesisService SpeechSynthesis { get; set; } = null!; + + [Inject] + public ILocalStorageService LocalStorage { get; set; } = null!; + + [Inject] + public ISessionStorageService SessionStorage { get; set; } = null!; + + [Inject] + public ILogger Logger { get; set; } = null!; + + protected override async Task OnInitializedAsync() + { + await GetVoicesAsync(); + SpeechSynthesis.OnVoicesChanged(() => GetVoicesAsync(true)); + + if (LocalStorage.GetItem(PreferredVoiceKey) + is { Length: > 0 } voice) + { + _selectedVoice = voice; + } + if (LocalStorage.GetItem(PreferredSpeedKey) + is double speed && speed > 0) + { + _voiceSpeed = speed; + } + if (SessionStorage.GetItem(TextKey) + is { Length: > 0 } text) + { + _text = text; + } + } + + async Task GetVoicesAsync(bool isFromCallback = false) + { + _voices = await SpeechSynthesis.GetVoicesAsync(); + if (_voices is { } && isFromCallback) + { + StateHasChanged(); + } + } + + void OnTextChanged(ChangeEventArgs args) => _text = args.Value?.ToString(); + + void OnVoiceSpeedChange(ChangeEventArgs args) => + _voiceSpeed = double.TryParse(args.Value?.ToString() ?? "1.5", out var speed) + ? speed : 1.5; + + void Speak() => SpeechSynthesis.Speak( + Utterance, + elapsedTime => + { + _elapsedTimeMessage = + $"Read aloud in {TimeSpan.FromMilliseconds(elapsedTime).Humanize()}."; + + StateHasChanged(); + }); + + void IDisposable.Dispose() + { + LocalStorage.SetItem(PreferredVoiceKey, _selectedVoice); + LocalStorage.SetItem(PreferredSpeedKey, _voiceSpeed); + SessionStorage.SetItem(TextKey, _text); + + SpeechSynthesis.UnsubscribeFromVoicesChanged(); + } +} \ No newline at end of file diff --git a/samples/Blazor.ExampleConsumer/Program.cs b/samples/Blazor.ExampleConsumer/Program.cs index 179015e9..04e14b90 100644 --- a/samples/Blazor.ExampleConsumer/Program.cs +++ b/samples/Blazor.ExampleConsumer/Program.cs @@ -18,7 +18,7 @@ builder.Services.AddLocalStorageServices(); builder.Services.AddSessionStorageServices(); builder.Services.AddGeolocationServices(); -//builder.Services.AddSpeechSynthesisServices(); +builder.Services.AddSpeechSynthesisServices(); // Custom library bits... builder.Services.AddSpeechRecognitionServices(); diff --git a/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj b/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj index 0f272886..138ed72f 100644 --- a/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj +++ b/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj @@ -16,7 +16,7 @@ - + diff --git a/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor b/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor index 67844614..be18f147 100644 --- a/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor +++ b/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor @@ -1,4 +1,4 @@ -@* @page "/speak" +@page "/speak" Text-to-speech @@ -46,4 +46,4 @@ Speak - *@ \ No newline at end of file + \ No newline at end of file diff --git a/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor.cs b/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor.cs index 338b857f..e4be4437 100644 --- a/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor.cs +++ b/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor.cs @@ -1,92 +1,92 @@ -//// Copyright (c) David Pine. All rights reserved. -//// Licensed under the MIT License. - -//namespace BlazorServer.ExampleConsumer.Pages; - -//public sealed partial class ReadToMe : IAsyncDisposable -//{ -// const string PreferredVoiceKey = "preferred-voice"; -// const string PreferredSpeedKey = "preferred-speed"; -// const string TextKey = "read-to-me-text"; - -// string? _text = "Blazorators is an open-source project that strives to simplify JavaScript interop in Blazor. JavaScript interoperability is possible by parsing TypeScript type declarations and using this metadata to output corresponding C# types."; -// SpeechSynthesisVoice[] _voices = Array.Empty(); -// readonly IList _voiceSpeeds = -// Enumerable.Range(0, 12).Select(i => (i + 1) * .25).ToList(); -// double _voiceSpeed = 1.5; -// string? _selectedVoice; -// string? _elapsedTimeMessage = null; - -// SpeechSynthesisUtterance Utterance => new() -// { -// Text = _text ?? "You forgot to try uttering some text.", -// Rate = _voiceSpeed, -// Volume = 1, -// Voice = _selectedVoice is { Length: > 0 } -// ? _voices?.FirstOrDefault(voice => voice.Name == _selectedVoice) -// : null -// }; - -// [Inject] -// public ISpeechSynthesisService SpeechSynthesis { get; set; } = null!; - -// [Inject] -// public ILocalStorageService LocalStorage { get; set; } = null!; - -// [Inject] -// public ISessionStorageService SessionStorage { get; set; } = null!; - -// [Inject] -// public ILogger Logger { get; set; } = null!; - -// protected override async Task OnAfterRenderAsync(bool firstRender) -// { -// if (firstRender is false) -// { -// return; -// } - -// await GetVoicesAsync(); - -// if (await LocalStorage.GetItemAsync(PreferredVoiceKey) -// is { Length: > 0 } voice) -// { -// _selectedVoice = voice; -// } -// if (await LocalStorage.GetItemAsync(PreferredSpeedKey) -// is { Length: > 0 } s && -// double.TryParse(s, out var speed) && speed > 0) -// { -// _voiceSpeed = speed; -// } -// if (await SessionStorage.GetItemAsync(TextKey) -// is { Length: > 0 } text) -// { -// _text = text; -// } -// } - -// async Task GetVoicesAsync(bool isFromCallback = false) -// { -// _voices = await SpeechSynthesis.GetVoicesAsync(); -// if (_voices is { } && isFromCallback) -// { -// StateHasChanged(); -// } -// } - -// void OnTextChanged(ChangeEventArgs args) => _text = args.Value?.ToString(); - -// void OnVoiceSpeedChange(ChangeEventArgs args) => -// _voiceSpeed = double.TryParse(args.Value?.ToString() ?? "1.5", out var speed) -// ? speed : 1.5; - -// ValueTask Speak() => SpeechSynthesis.SpeakAsync(Utterance); - -// async ValueTask IAsyncDisposable.DisposeAsync() -// { -// await LocalStorage.SetItemAsync(PreferredVoiceKey, _selectedVoice!); -// await LocalStorage.SetItemAsync(PreferredSpeedKey, _voiceSpeed.ToString()); -// await SessionStorage.SetItemAsync(TextKey, _text!); -// } -//} \ No newline at end of file +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. + +namespace BlazorServer.ExampleConsumer.Pages; + +public sealed partial class ReadToMe : IAsyncDisposable +{ + const string PreferredVoiceKey = "preferred-voice"; + const string PreferredSpeedKey = "preferred-speed"; + const string TextKey = "read-to-me-text"; + + string? _text = "Blazorators is an open-source project that strives to simplify JavaScript interop in Blazor. JavaScript interoperability is possible by parsing TypeScript type declarations and using this metadata to output corresponding C# types."; + SpeechSynthesisVoice[] _voices = Array.Empty(); + readonly IList _voiceSpeeds = + Enumerable.Range(0, 12).Select(i => (i + 1) * .25).ToList(); + double _voiceSpeed = 1.5; + string? _selectedVoice; + string? _elapsedTimeMessage = null; + + SpeechSynthesisUtterance Utterance => new() + { + Text = _text ?? "You forgot to try uttering some text.", + Rate = _voiceSpeed, + Volume = 1, + Voice = _selectedVoice is { Length: > 0 } + ? _voices?.FirstOrDefault(voice => voice.Name == _selectedVoice) + : null + }; + + [Inject] + public ISpeechSynthesisService SpeechSynthesis { get; set; } = null!; + + [Inject] + public ILocalStorageService LocalStorage { get; set; } = null!; + + [Inject] + public ISessionStorageService SessionStorage { get; set; } = null!; + + [Inject] + public ILogger Logger { get; set; } = null!; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender is false) + { + return; + } + + await GetVoicesAsync(); + + if (await LocalStorage.GetItemAsync(PreferredVoiceKey) + is { Length: > 0 } voice) + { + _selectedVoice = voice; + } + if (await LocalStorage.GetItemAsync(PreferredSpeedKey) + is { Length: > 0 } s && + double.TryParse(s, out var speed) && speed > 0) + { + _voiceSpeed = speed; + } + if (await SessionStorage.GetItemAsync(TextKey) + is { Length: > 0 } text) + { + _text = text; + } + } + + async Task GetVoicesAsync(bool isFromCallback = false) + { + _voices = await SpeechSynthesis.GetVoicesAsync(); + if (_voices is { } && isFromCallback) + { + StateHasChanged(); + } + } + + void OnTextChanged(ChangeEventArgs args) => _text = args.Value?.ToString(); + + void OnVoiceSpeedChange(ChangeEventArgs args) => + _voiceSpeed = double.TryParse(args.Value?.ToString() ?? "1.5", out var speed) + ? speed : 1.5; + + ValueTask Speak() => SpeechSynthesis.SpeakAsync(Utterance); + + async ValueTask IAsyncDisposable.DisposeAsync() + { + await LocalStorage.SetItemAsync(PreferredVoiceKey, _selectedVoice!); + await LocalStorage.SetItemAsync(PreferredSpeedKey, _voiceSpeed.ToString()); + await SessionStorage.SetItemAsync(TextKey, _text!); + } +} \ No newline at end of file diff --git a/samples/BlazorServer.ExampleConsumer/Program.cs b/samples/BlazorServer.ExampleConsumer/Program.cs index 65c91aea..f58e5830 100644 --- a/samples/BlazorServer.ExampleConsumer/Program.cs +++ b/samples/BlazorServer.ExampleConsumer/Program.cs @@ -11,7 +11,7 @@ builder.Services.AddLocalStorageServices(); builder.Services.AddSessionStorageServices(); builder.Services.AddGeolocationServices(); -//builder.Services.AddSpeechSynthesisServices(); +builder.Services.AddSpeechSynthesisServices(); // Custom library bits... builder.Services.AddSpeechRecognitionServices(); diff --git a/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs b/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs index ac08fe5a..d1c0ab70 100644 --- a/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs +++ b/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs @@ -314,7 +314,6 @@ internal SourceBuilder AppendUsingDeclarations() _builder.Append($"using System.Threading.Tasks;{NewLine}"); } - _builder.Append($"#nullable enable{NewLine}"); _builder.Append(NewLine); return this; diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs b/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs index f596bd12..4c848dac 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs @@ -40,7 +40,9 @@ internal record CSharpMethod( /// /// Gets all dependent types of this C# method, including those from parameters and JavaScript dependencies. /// - public IImmutableSet AllDependentTypes => ParameterDefinitions.GetDependentTypes() + public IImmutableSet AllDependentTypes => DependentTypes + .Select(kvp => new DependentType(kvp.Key, kvp.Value)) + .Concat(ParameterDefinitions.GetDependentTypes()) .Concat(JavaScriptMethodDependency?.ParameterDefinitions.GetDependentTypes()) .ToImmutableHashSet(DependentTypeComparer.Default); } \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs index ec1006c2..dadd167a 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs @@ -122,6 +122,8 @@ private static void AppendMethodParameters(SourceBuilder builder, CSharpMethod m builder.AppendRaw($"{parameterString},"); } } + + builder.DecreaseIndentation(); } private static void AppendNonPureMethod(SourceBuilder builder, CSharpMethod method, MethodBuilderDetails details, GeneratorOptions options) diff --git a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs index df5451fd..0f44acf3 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.Interfaces.cs @@ -1,6 +1,7 @@ // Copyright (c) David Pine. All rights reserved. // Licensed under the MIT License. +using System.Reflection; using Blazor.SourceGenerators.TypeScript.Types; namespace Blazor.SourceGenerators.Parsers; @@ -91,9 +92,21 @@ internal CSharpTopLevelObject ToTopLevelObject(DeclarationStatement declaration) return csharpTopLevelObject; } - private static string GetNodeText(INode propertyTypeNode) + private static string CleanseType(string type) { - return propertyTypeNode.GetText().ToString().Trim(); + return type + .Replace(" | null", "") + .Replace(" | undefined", ""); + } + + private static string GetNodeText(INode node) + { + return node.GetText().ToString().Trim(); + } + + private static bool IsNullableType(string type) + { + return type.EndsWith(" | null") || type.EndsWith(" | undefined"); } private IEnumerable ParseMethods(string rawTypeName, IEnumerable objectMethods, Action appendDependency) @@ -110,6 +123,12 @@ private IEnumerable ParseMethods(string rawTypeName, IEnumerable ParseMethods(string rawTypeName, IEnumerable ParseProperties(IEnumerable objectProp ICollection properties = []; foreach (var property in objectProperties.Cast()) { - var isReadonly = property.Modifiers.Exists(modifier => modifier.Kind is TypeScriptSyntaxKind.ReadonlyKeyword); - var isNullable = property.QuestionToken is not null; - var propertyName = property.Identifier; var propertyTypeNode = property.Children[property.Children.Count - 1]; @@ -191,15 +211,28 @@ private IEnumerable ParseProperties(IEnumerable objectProp var propertyType = propertyTypeNode switch { - _ when isNullable => GetNodeText(propertyTypeNode).Replace(" | null", ""), _ => GetNodeText(propertyTypeNode) }; + var isReadonly = property.Modifiers.Exists(modifier => modifier.Kind is TypeScriptSyntaxKind.ReadonlyKeyword); + var isNullable = property.QuestionToken is not null || IsNullableType(propertyType); + + if (isNullable) + { + propertyType = CleanseType(propertyType); + } + if (propertyName is null || string.IsNullOrEmpty(propertyType)) { continue; } + // FIXME: For now ignore all properties that starts with "on" + if (propertyName.StartsWith("on")) + { + continue; + } + var csharpProperty = new CSharpProperty(propertyName, propertyType, isNullable, isReadonly); properties.Add(csharpProperty); diff --git a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs index 60267859..43421a55 100644 --- a/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs +++ b/src/Blazor.SourceGenerators/Parsers/TypeDeclarationParser.cs @@ -5,12 +5,12 @@ namespace Blazor.SourceGenerators.Parsers; internal sealed partial class TypeDeclarationParser { - static readonly Lazy s_defaultParser = + static readonly Lazy _parser = new(valueFactory: () => new TypeDeclarationParser(TypeDeclarationReader.Default)); readonly TypeDeclarationReader _reader; - internal static TypeDeclarationParser Default => s_defaultParser.Value; + internal static TypeDeclarationParser Default => _parser.Value; internal TypeDeclarationParser(TypeDeclarationReader reader) => _reader = reader; From b8a1428ef7016898022fc02c03800f288d78f06b Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Thu, 18 Jul 2024 21:30:28 +0200 Subject: [PATCH 31/41] feat: handle promises on webassembly, fix indentation for non pure callbacks, fix new lines in dependent types --- .../Builders/MethodBuilderDetails.cs | 15 +- .../Builders/SourceBuilder.cs | 7 +- .../CSharp/CSharpMethod.cs | 7 +- .../CSharp/CSharpObject.cs | 15 +- .../CSharp/CSharpTopLevelObject.cs | 65 +++++---- .../Extensions/CSharpMethodExtensions.cs | 130 +++++++++--------- .../LibDomParserInterfacesTests.cs | 6 + 7 files changed, 143 insertions(+), 102 deletions(-) diff --git a/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs b/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs index 3b5011e7..34f3147a 100644 --- a/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs +++ b/src/Blazor.SourceGenerators/Builders/MethodBuilderDetails.cs @@ -23,6 +23,7 @@ namespace Blazor.SourceGenerators.Builders; internal readonly record struct MethodBuilderDetails( CSharpMethod Method, bool IsVoid, + bool IsAsync, bool IsPrimitiveType, bool IsGenericReturnType, bool ContainsGenericParameters, @@ -74,6 +75,7 @@ internal static MethodBuilderDetails Create(CSharpMethod method, GeneratorOption return new MethodBuilderDetails( Method: method, IsVoid: method.IsVoid, + IsAsync: method.IsAsync, IsPrimitiveType: isPrimitiveType, IsGenericReturnType: isGenericReturnType, ContainsGenericParameters: containsGenericParameters, @@ -113,6 +115,17 @@ private static (string Suffix, string ExtendingType) DetermineSuffixAndExtending { return ("Async", "IJSRuntime"); } - return options.IsWebAssembly ? ("", "IJSInProcessRuntime") : ("Async", "IJSRuntime"); + + if (!options.IsWebAssembly) + { + return ("Async", "IJSRuntime"); + } + + if (options.IsWebAssembly && method.IsAsync) + { + return ("Async", "IJSInProcessRuntime"); + } + + return ("", "IJSInProcessRuntime"); } } \ No newline at end of file diff --git a/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs b/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs index d1c0ab70..1c14d623 100644 --- a/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs +++ b/src/Blazor.SourceGenerators/Builders/SourceBuilder.cs @@ -175,13 +175,18 @@ internal SourceBuilder AppendInternalImplementationDeclaration() return this; } - internal SourceBuilder AppendLine() + internal SourceBuilder AppendLine(bool postIncreaseIndentation = false) { // 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); + if (postIncreaseIndentation) + { + IncreaseIndentation(); + } + return this; } diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs b/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs index 4c848dac..b3ef4771 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpMethod.cs @@ -30,7 +30,12 @@ internal record CSharpMethod( /// /// Indicates whether the method returns void. /// - public bool IsVoid => RawReturnTypeName == "void"; + public bool IsVoid => (IsAsync ? RawReturnTypeName.ExtractGenericType() : RawReturnTypeName) == "void"; + + /// + /// Indicates wether the method needs an async invocation + /// + public bool IsAsync => RawReturnTypeName.StartsWith("Promise"); /// /// The collection of types that this object depends on. diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpObject.cs b/src/Blazor.SourceGenerators/CSharp/CSharpObject.cs index 2548f068..637b681b 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpObject.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpObject.cs @@ -48,10 +48,9 @@ public override string ToString() builder.Append($"/// \r\n"); builder.Append($"public class {TypeName}\r\n{{\r\n"); - foreach (var (index, kvp) - in Properties.Select((kvp, index) => (index, kvp))) + foreach (var (index, property) in Properties.Select((property, index) => (index, property))) { - var (memberName, member) = (kvp.Key, kvp.Value); + var (memberName, member) = (property.Key, property.Value); var typeName = member.MappedTypeName; var nullableExpression = member.IsNullable && !typeName.EndsWith("?") ? "?" : ""; var trivia = member.IsArray ? "[]" : ""; @@ -66,9 +65,15 @@ in Properties.Select((kvp, index) => (index, kvp))) builder.Append($" [JsonPropertyName(\"{memberName}\")]\r\n"); builder.Append($" public {typeName}{trivia}{nullableExpression} {csharpMemberName} {{ get; set; }}{statementTerminator}\r\n"); + var isTimestamp = member.RawTypeName is "DOMTimeStamp" or "DOMTimeStamp | null" or "EpochTimeStamp" or "EpochTimeStamp | null"; + + if (index <= Properties.Count - 2 || isTimestamp) + { + builder.Append($"\r\n"); + } + // Add readonly property for converting DOMTimeStamp (long) to DateTime. - if (member.RawTypeName is "DOMTimeStamp" or "DOMTimeStamp | null" - or "EpochTimeStamp" or "EpochTimeStamp | null") + if (isTimestamp) { builder.Append($" /// \r\n"); builder.Append($" /// Source-generated property representing the {TypeName}.{memberName} value, \r\n"); diff --git a/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs index dadd167a..cbf30bbe 100644 --- a/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs +++ b/src/Blazor.SourceGenerators/CSharp/CSharpTopLevelObject.cs @@ -45,7 +45,7 @@ internal string ToInterfaceString(GeneratorOptions options, string? namespaceStr var methodLevel = builder.IndentationLevel; // Methods - foreach (var method in Methods) + foreach (var (index, method) in Methods.Select()) { var details = MethodBuilderDetails.Create(method, options); builder.ResetIndentiationTo(methodLevel); @@ -66,7 +66,7 @@ internal string ToInterfaceString(GeneratorOptions options, string? namespaceStr if (hasParameters) { - AppendMethodParameters(builder, method, details, options, suffix: ");"); + AppendMethodParameters(builder, method, details, options, suffix: ");", appendNewLine: true); } else { @@ -78,15 +78,25 @@ internal string ToInterfaceString(GeneratorOptions options, string? namespaceStr AppendNonPureMethod(builder, method, details, options); } - builder.AppendLine(); + if (!index.IsLast) + { + builder.AppendLine(); + } } // Properties - foreach (var property in Properties) + foreach (var (index, property) in Properties.Select()) { - if (!property.IsIndexer) + if (property.IsIndexer) { - AppendProperty(builder, property, options); + continue; + } + + AppendProperty(builder, property, options); + + if (!index.IsLast) + { + builder.AppendLine(); } } @@ -96,7 +106,7 @@ internal string ToInterfaceString(GeneratorOptions options, string? namespaceStr return TryFormatCSharpSourceText(builder.ToSourceCodeString()); } - private static void AppendMethodParameters(SourceBuilder builder, CSharpMethod method, MethodBuilderDetails details, GeneratorOptions options, string suffix = "", bool asDelegate = false) + private static void AppendMethodParameters(SourceBuilder builder, CSharpMethod method, MethodBuilderDetails details, GeneratorOptions options, string suffix = "", bool asDelegate = false, bool appendNewLine = false) { foreach (var (pi, parameter) in method.ParameterDefinitions.Select()) { @@ -110,11 +120,11 @@ private static void AppendMethodParameters(SourceBuilder builder, CSharpMethod m if (details.IsSerializable) { builder.AppendRaw($"{parameterString},") - .AppendRaw($"JsonSerializerOptions? options = null{suffix}", appendNewLine: false); + .AppendRaw($"JsonSerializerOptions? options = null{suffix}", appendNewLine); } else { - builder.AppendRaw($"{parameterString}{suffix}", appendNewLine: false); + builder.AppendRaw($"{parameterString}{suffix}", appendNewLine); } } else @@ -168,8 +178,7 @@ private static void AppendNonPureMethod(SourceBuilder builder, CSharpMethod meth var isGenericType = parameter.IsGenericParameter(method.RawName, options); if (pi.IsLast) { - builder.AppendRaw($"{parameter.ToActionString(isGenericType)});") - .AppendLine(); + builder.AppendRaw($"{parameter.ToActionString(isGenericType)});"); } else { @@ -210,17 +219,29 @@ internal string ToImplementationString(GeneratorOptions options, string? namespa builder.AppendConditionalDelegateCallbackMethods(Methods); // Methods - foreach (var method in Methods) + foreach (var (index, method) in Methods.Select()) { AppendImplementationMethod(builder, method, options, methodLevel); + + if (!index.IsLast) + { + builder.AppendLine(); + } } // Properties foreach (var (index, property) in Properties.Select()) { - if (!property.IsIndexer) + if (property.IsIndexer) + { + continue; + } + + AppendImplementationProperty(builder, property, options, methodLevel); + + if (!index.IsLast) { - AppendImplementationProperty(builder, property, options, methodLevel, index); + builder.AppendLine(); } } @@ -293,8 +314,6 @@ private static void AppendImplementationMethod(SourceBuilder builder, CSharpMeth AppendActionCallbackMethodImplementation(builder, method, details, options); } - - builder.AppendLine(); } private static void AppendActionCallbackMethodImplementation(SourceBuilder builder, CSharpMethod method, MethodBuilderDetails details, GeneratorOptions options) @@ -314,7 +333,7 @@ private static void AppendActionCallbackMethodImplementation(SourceBuilder build builder.AppendRaw(") =>", appendNewLine: true, omitIndentation: true); } - builder.AppendLine(); + builder.AppendLine(postIncreaseIndentation: true); if (details.IsVoid) { @@ -477,7 +496,7 @@ private static void AppendNonPureMethodImplementation(SourceBuilder builder, CSh builder.AppendRaw(") where TComponent : class =>", appendNewLine: true, omitIndentation: true); } - builder.AppendLine(); + builder.AppendLine(postIncreaseIndentation: true); if (details.IsVoid) { @@ -516,11 +535,8 @@ private static void AppendNonPureJavascriptParameters(CSharpMethod method, Sourc builder.DecreaseIndentation(); } - private static void AppendImplementationProperty(SourceBuilder builder, CSharpProperty property, GeneratorOptions options, int methodLevel, Iteration index) + private static void AppendImplementationProperty(SourceBuilder builder, CSharpProperty property, GeneratorOptions options, int methodLevel) { - if (index.IsFirst) builder.AppendLine(); - if (property.IsIndexer) return; - builder.ResetIndentiationTo(methodLevel); var details = PropertyBuilderDetails.Create(property, options); @@ -529,11 +545,6 @@ private static void AppendImplementationProperty(SourceBuilder builder, CSharpPr .AppendRaw($"{details.ReturnType} {builder.InterfaceName}.{details.CSharpPropertyName} =>", postIncreaseIndentation: true) .AppendRaw($"_javaScript.Invoke{details.Suffix}{details.GenericTypeArgs}(", postIncreaseIndentation: true) .AppendRaw($"\"eval\", \"{details.FullyQualifiedJavaScriptIdentifier}\");"); - - if (!index.IsLast) - { - builder.AppendLine(); - } } /// diff --git a/src/Blazor.SourceGenerators/Extensions/CSharpMethodExtensions.cs b/src/Blazor.SourceGenerators/Extensions/CSharpMethodExtensions.cs index 23a97fcd..5f6f2a0f 100644 --- a/src/Blazor.SourceGenerators/Extensions/CSharpMethodExtensions.cs +++ b/src/Blazor.SourceGenerators/Extensions/CSharpMethodExtensions.cs @@ -8,50 +8,17 @@ namespace Blazor.SourceGenerators.Extensions; internal static class CSharpMethodExtensions { - internal static bool IsJavaScriptOverride(this CSharpMethod method, GeneratorOptions options) - { - var methodName = method.RawName.LowerCaseFirstLetter(); - return Array.Exists(options.PureJavaScriptOverrides ?? [], overriddenMethodName => overriddenMethodName == methodName); - } - - internal static bool IsGenericReturnType(this CSharpMethod method, GeneratorOptions options) => - 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(":")) - { - return false; - } - - // If the descriptor is the method name - return descriptor == method.RawName; - }); - - internal static bool IsGenericParameter(string methodName, CSharpType parameter, GeneratorOptions options) => - Array.Exists(options.GenericMethodDescriptors ?? [], descriptor => - { - if (!descriptor.StartsWith(methodName)) - { - return false; - } - - if (descriptor.Contains(":")) - { - var nameParamPair = descriptor.Split(':'); - return nameParamPair[1].StartsWith(parameter.RawName); - } - - return false; - }); - - internal static (string ReturnType, string BareType) GetMethodTypes( - this CSharpMethod method, GeneratorOptions options, bool isGenericReturnType, bool isPrimitiveType) + internal static (string ReturnType, string BareType) GetMethodTypes(this CSharpMethod method, GeneratorOptions options, bool isGenericReturnType, bool isPrimitiveType) { var primitiveType = isPrimitiveType ? Primitives.Instance[method.RawReturnTypeName] : method.RawReturnTypeName; + if (method.IsAsync) + { + primitiveType = primitiveType.ExtractGenericType(); + } + if (!method.IsVoid && isGenericReturnType) { var nullable = @@ -70,23 +37,15 @@ internal static (string ReturnType, string BareType) GetMethodTypes( { 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 switch { - returnType = method.RawReturnTypeName; - } + { IsVoid: true, IsAsync: false } => "void", + { IsVoid: true, IsAsync: true } => "ValueTask", + { IsVoid: false, IsAsync: true } => GetGenericValueTask(method), + _ => method.RawReturnTypeName + }; } return (returnType, primitiveType); @@ -98,26 +57,63 @@ internal static (string ReturnType, string BareType) GetMethodTypes( { 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 = method switch { - returnType = $"ValueTask<{method.RawReturnTypeName}>"; - } + { IsVoid: true, IsAsync: false } => "ValueTask", + { IsVoid: true, IsAsync: true } => "ValueTask", + { IsVoid: false, IsAsync: true } => GetGenericValueTask(method), + _ => $"ValueTask<{method.RawReturnTypeName}>" + }; } return (returnType, primitiveType); } } + + internal static bool IsGenericParameter(string methodName, CSharpType parameter, GeneratorOptions options) => + Array.Exists(options.GenericMethodDescriptors ?? [], descriptor => + { + if (!descriptor.StartsWith(methodName)) + { + return false; + } + + if (descriptor.Contains(":")) + { + var nameParamPair = descriptor.Split(':'); + return nameParamPair[1].StartsWith(parameter.RawName); + } + + return false; + }); + + internal static bool IsGenericReturnType(this CSharpMethod method, GeneratorOptions options) => + 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(":")) + { + return false; + } + + // If the descriptor is the method name + return descriptor == method.RawName; + }); + + internal static bool IsJavaScriptOverride(this CSharpMethod method, GeneratorOptions options) + { + var methodName = method.RawName.LowerCaseFirstLetter(); + return Array.Exists(options.PureJavaScriptOverrides ?? [], overriddenMethodName => overriddenMethodName == methodName); + } + + private static string GetGenericValueTask(CSharpMethod method) + { + var genericType = method.RawReturnTypeName.ExtractGenericType(); + return Primitives.IsPrimitiveType(genericType) + ? $"ValueTask<{Primitives.Instance[genericType]}>" + : $"ValueTask<{genericType}>"; + } } \ No newline at end of file diff --git a/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs b/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs index 2f1ed068..6dbe5f6f 100644 --- a/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs +++ b/tests/Blazor.SourceGenerators.Tests/LibDomParserInterfacesTests.cs @@ -29,31 +29,37 @@ public class MediaKeySystemConfiguration /// [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. /// From 3808f6ffe70e63b0f7e6ab24e95f8c3b4c2412c8 Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Thu, 18 Jul 2024 21:41:21 +0200 Subject: [PATCH 32/41] feat: add initial clipboard api for webassembly --- blazorators.sln | 7 ++ .../Blazor.ExampleConsumer.csproj | 1 + samples/Blazor.ExampleConsumer/Program.cs | 1 + .../Blazor.Clipboard.WebAssembly.csproj | 77 +++++++++++++++++++ .../ClipboardItem.cs | 16 ++++ .../ClipboardItems.cs | 8 ++ .../IClipboardService.cs | 10 +++ 7 files changed, 120 insertions(+) create mode 100644 src/Blazor.Clipboard.WebAssembly/Blazor.Clipboard.WebAssembly.csproj create mode 100644 src/Blazor.Clipboard.WebAssembly/ClipboardItem.cs create mode 100644 src/Blazor.Clipboard.WebAssembly/ClipboardItems.cs create mode 100644 src/Blazor.Clipboard.WebAssembly/IClipboardService.cs diff --git a/blazorators.sln b/blazorators.sln index e706b853..5554e198 100644 --- a/blazorators.sln +++ b/blazorators.sln @@ -56,6 +56,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorServer.ExampleConsume EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazor.CacheStorage.WebAssembly", "src\Blazor.CacheStorage.WebAssembly\Blazor.CacheStorage.WebAssembly.csproj", "{38FE259D-0B98-495E-B376-D0F3C9CA12B0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazor.Clipboard.WebAssembly", "src\Blazor.Clipboard.WebAssembly\Blazor.Clipboard.WebAssembly.csproj", "{A6CFFD60-9533-4B6D-83EB-170B025A6DEC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -134,6 +136,10 @@ Global {38FE259D-0B98-495E-B376-D0F3C9CA12B0}.Debug|Any CPU.Build.0 = Debug|Any CPU {38FE259D-0B98-495E-B376-D0F3C9CA12B0}.Release|Any CPU.ActiveCfg = Release|Any CPU {38FE259D-0B98-495E-B376-D0F3C9CA12B0}.Release|Any CPU.Build.0 = Release|Any CPU + {A6CFFD60-9533-4B6D-83EB-170B025A6DEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6CFFD60-9533-4B6D-83EB-170B025A6DEC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6CFFD60-9533-4B6D-83EB-170B025A6DEC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6CFFD60-9533-4B6D-83EB-170B025A6DEC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -157,6 +163,7 @@ Global {4D04B168-6971-4660-BC22-FA7F18150018} = {A644CEC3-BD94-4EB6-9BF0-86B562806BF9} {31D54C48-6325-4591-8612-87A917D49028} = {91A35318-B03F-4D41-AE18-2C21B9D9C3F3} {38FE259D-0B98-495E-B376-D0F3C9CA12B0} = {537EB83C-6982-40B0-801A-479DF3B17DBE} + {A6CFFD60-9533-4B6D-83EB-170B025A6DEC} = {537EB83C-6982-40B0-801A-479DF3B17DBE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3F86284A-32D2-4F79-B23C-7A0CB8775971} diff --git a/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj b/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj index 557a0956..6247c587 100644 --- a/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj +++ b/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj @@ -13,6 +13,7 @@ + diff --git a/samples/Blazor.ExampleConsumer/Program.cs b/samples/Blazor.ExampleConsumer/Program.cs index 04e14b90..3f53ee7f 100644 --- a/samples/Blazor.ExampleConsumer/Program.cs +++ b/samples/Blazor.ExampleConsumer/Program.cs @@ -19,6 +19,7 @@ builder.Services.AddSessionStorageServices(); builder.Services.AddGeolocationServices(); builder.Services.AddSpeechSynthesisServices(); +builder.Services.AddClipboardServices(); // Custom library bits... builder.Services.AddSpeechRecognitionServices(); diff --git a/src/Blazor.Clipboard.WebAssembly/Blazor.Clipboard.WebAssembly.csproj b/src/Blazor.Clipboard.WebAssembly/Blazor.Clipboard.WebAssembly.csproj new file mode 100644 index 00000000..e5306177 --- /dev/null +++ b/src/Blazor.Clipboard.WebAssembly/Blazor.Clipboard.WebAssembly.csproj @@ -0,0 +1,77 @@ + + + + $(DefaultTargetFrameworks) + enable + enable + Source generated JavaScript interop for the browser's clipboard 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.Clipboard.WebAssembly + A C# source-generated Razor class library implementation of the native browser's clipboard API available as a DI-ready service. + Blazor.Clipboard.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.Clipboard.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 + + + + + + + + + + + + + + + + diff --git a/src/Blazor.Clipboard.WebAssembly/ClipboardItem.cs b/src/Blazor.Clipboard.WebAssembly/ClipboardItem.cs new file mode 100644 index 00000000..aa6ac6e3 --- /dev/null +++ b/src/Blazor.Clipboard.WebAssembly/ClipboardItem.cs @@ -0,0 +1,16 @@ +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.JSInterop; + +public class ClipboardItem +{ + public IReadOnlyList Types { get; private set; } = []; + + public async Task GetTypeAsync(string type) + { + // Implement your logic here to fetch Blob data based on type + // This is just a placeholder + return await Task.FromResult(Array.Empty()); + } +} \ No newline at end of file diff --git a/src/Blazor.Clipboard.WebAssembly/ClipboardItems.cs b/src/Blazor.Clipboard.WebAssembly/ClipboardItems.cs new file mode 100644 index 00000000..d58fc874 --- /dev/null +++ b/src/Blazor.Clipboard.WebAssembly/ClipboardItems.cs @@ -0,0 +1,8 @@ +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.JSInterop; + +public class ClipboardItems : List +{ +} \ No newline at end of file diff --git a/src/Blazor.Clipboard.WebAssembly/IClipboardService.cs b/src/Blazor.Clipboard.WebAssembly/IClipboardService.cs new file mode 100644 index 00000000..173135f1 --- /dev/null +++ b/src/Blazor.Clipboard.WebAssembly/IClipboardService.cs @@ -0,0 +1,10 @@ +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.JSInterop; + +[JSAutoInterop( + TypeName = "Clipboard", + Implementation = "window.navigator.clipboard", + Url = "https://developer.mozilla.org/docs/Web/API/Clipboard")] +public partial interface IClipboardService; \ No newline at end of file From bcf284fe1b7f2dd7f25384f312628a5e34a0a9fe Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Thu, 18 Jul 2024 21:44:38 +0200 Subject: [PATCH 33/41] feat: add initial clipboard api for blazor server --- blazorators.sln | 9 +- .../BlazorServer.ExampleConsumer.csproj | 1 + .../BlazorServer.ExampleConsumer/Program.cs | 1 + src/Blazor.Clipboard.WebAssembly/README.md | 93 +++++++++++++++++++ src/Blazor.Clipboard/Blazor.Clipboard.csproj | 77 +++++++++++++++ src/Blazor.Clipboard/ClipboardItem.cs | 16 ++++ src/Blazor.Clipboard/ClipboardItems.cs | 8 ++ src/Blazor.Clipboard/IClipboardService.cs | 11 +++ src/Blazor.Clipboard/README.md | 93 +++++++++++++++++++ 9 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 src/Blazor.Clipboard.WebAssembly/README.md create mode 100644 src/Blazor.Clipboard/Blazor.Clipboard.csproj create mode 100644 src/Blazor.Clipboard/ClipboardItem.cs create mode 100644 src/Blazor.Clipboard/ClipboardItems.cs create mode 100644 src/Blazor.Clipboard/IClipboardService.cs create mode 100644 src/Blazor.Clipboard/README.md diff --git a/blazorators.sln b/blazorators.sln index 5554e198..f55af5ea 100644 --- a/blazorators.sln +++ b/blazorators.sln @@ -56,7 +56,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorServer.ExampleConsume EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazor.CacheStorage.WebAssembly", "src\Blazor.CacheStorage.WebAssembly\Blazor.CacheStorage.WebAssembly.csproj", "{38FE259D-0B98-495E-B376-D0F3C9CA12B0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazor.Clipboard.WebAssembly", "src\Blazor.Clipboard.WebAssembly\Blazor.Clipboard.WebAssembly.csproj", "{A6CFFD60-9533-4B6D-83EB-170B025A6DEC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazor.Clipboard.WebAssembly", "src\Blazor.Clipboard.WebAssembly\Blazor.Clipboard.WebAssembly.csproj", "{A6CFFD60-9533-4B6D-83EB-170B025A6DEC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazor.Clipboard", "src\Blazor.Clipboard\Blazor.Clipboard.csproj", "{2218709E-9A36-4578-BA30-D32A713C9481}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -140,6 +142,10 @@ Global {A6CFFD60-9533-4B6D-83EB-170B025A6DEC}.Debug|Any CPU.Build.0 = Debug|Any CPU {A6CFFD60-9533-4B6D-83EB-170B025A6DEC}.Release|Any CPU.ActiveCfg = Release|Any CPU {A6CFFD60-9533-4B6D-83EB-170B025A6DEC}.Release|Any CPU.Build.0 = Release|Any CPU + {2218709E-9A36-4578-BA30-D32A713C9481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2218709E-9A36-4578-BA30-D32A713C9481}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2218709E-9A36-4578-BA30-D32A713C9481}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2218709E-9A36-4578-BA30-D32A713C9481}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -164,6 +170,7 @@ Global {31D54C48-6325-4591-8612-87A917D49028} = {91A35318-B03F-4D41-AE18-2C21B9D9C3F3} {38FE259D-0B98-495E-B376-D0F3C9CA12B0} = {537EB83C-6982-40B0-801A-479DF3B17DBE} {A6CFFD60-9533-4B6D-83EB-170B025A6DEC} = {537EB83C-6982-40B0-801A-479DF3B17DBE} + {2218709E-9A36-4578-BA30-D32A713C9481} = {537EB83C-6982-40B0-801A-479DF3B17DBE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3F86284A-32D2-4F79-B23C-7A0CB8775971} diff --git a/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj b/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj index 138ed72f..17456ada 100644 --- a/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj +++ b/samples/BlazorServer.ExampleConsumer/BlazorServer.ExampleConsumer.csproj @@ -11,6 +11,7 @@ + diff --git a/samples/BlazorServer.ExampleConsumer/Program.cs b/samples/BlazorServer.ExampleConsumer/Program.cs index f58e5830..60d44326 100644 --- a/samples/BlazorServer.ExampleConsumer/Program.cs +++ b/samples/BlazorServer.ExampleConsumer/Program.cs @@ -12,6 +12,7 @@ builder.Services.AddSessionStorageServices(); builder.Services.AddGeolocationServices(); builder.Services.AddSpeechSynthesisServices(); +builder.Services.AddClipboardServices(); // Custom library bits... builder.Services.AddSpeechRecognitionServices(); diff --git a/src/Blazor.Clipboard.WebAssembly/README.md b/src/Blazor.Clipboard.WebAssembly/README.md new file mode 100644 index 00000000..8b7f34d9 --- /dev/null +++ b/src/Blazor.Clipboard.WebAssembly/README.md @@ -0,0 +1,93 @@ +# Blazorators: The Source Generated `geolocation` JavaScript Interop library for Blazor + +The [`Blazor.Geolocation`](https://www.nuget.org/packages/Blazor.Geolocation) package consumes the [`Blazor.SourceGenerators`](https://www.nuget.org/packages/Blazor.SourceGenerators) package. It exposes a source generated `IGeolocation` interface specific to Blazor WebAssembly and the [`geolocation`](https://developer.mozilla.org/docs/Web/API/Geolocation) Web API. + +## Get started + +After the NuGet package is added as a reference, call the `AddGeolocationServices` method to register the `IGeolocationService` service type. + +```csharp +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddGeolocationServices(); +builder.Services.AddRazorPages(); +builder.Services.AddServerSideBlazor(); + +var app = builder.Build(); + +app.UseHttpsRedirection(); +app.UseStaticFiles(); +app.UseRouting(); + +app.MapControllers(); +app.MapBlazorHub(); +app.MapFallbackToPage("/_Host"); + +app.Run(); +``` + +Anywhere needed within your Razor component, or Blazor client code — either `@inject` or `[Inject]` the `IGeolocationService` type. The interface takes the following shape: + +```csharp +#nullable enable +namespace Microsoft.JSInterop; + +/// +/// Source generated interface definition of the Geolocation type. +/// +public interface IGeolocationService +{ + /// + /// Source generated implementation of window.navigator.geolocation.clearWatch. + /// + /// + ValueTask ClearWatchAsync(double watchId); + + /// + /// Source generated implementation of window.navigator.geolocation.getCurrentPosition. + /// + /// + /// The calling Razor (or Blazor) component. + /// Expects the name of a + /// "JSInvokableAttribute" C# method with the following + /// System.Action{GeolocationPosition}". + /// Expects the name of a + /// "JSInvokableAttribute" C# method with the following + /// System.Action{GeolocationPositionError}". + /// The PositionOptions value. + ValueTask GetCurrentPositionAsync( + TComponent component, + string onSuccessCallbackMethodName, + string? onErrorCallbackMethodName = null, + PositionOptions? options = null) + where TComponent : class; + + /// + /// Source generated implementation of window.navigator.geolocation.watchPosition. + /// + /// + /// The calling Razor (or Blazor) component. + /// Expects the name of a + /// "JSInvokableAttribute" C# method with the following + /// System.Action{GeolocationPosition}". + /// Expects the name of a + /// "JSInvokableAttribute" C# method with the following + /// System.Action{GeolocationPositionError}". + /// The PositionOptions value. + ValueTask WatchPositionAsync( + TComponent component, + string onSuccessCallbackMethodName, + string? onErrorCallbackMethodName = null, + PositionOptions? options = null) + where TComponent : class; +} +``` + +### Add JavaScript dependency + +In the *_Host.cshtml* file, add the following: + +```html + +``` \ No newline at end of file diff --git a/src/Blazor.Clipboard/Blazor.Clipboard.csproj b/src/Blazor.Clipboard/Blazor.Clipboard.csproj new file mode 100644 index 00000000..8bc76864 --- /dev/null +++ b/src/Blazor.Clipboard/Blazor.Clipboard.csproj @@ -0,0 +1,77 @@ + + + + $(DefaultTargetFrameworks) + enable + enable + Source generated JavaScript interop for the browser's clipboard 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.Clipboard + A C# source-generated Razor class library implementation of the native browser's clipboard API available as a DI-ready service. + Blazor.Clipboard + 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.Clipboard. + 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 + + + + + + + + + + + + + + + + diff --git a/src/Blazor.Clipboard/ClipboardItem.cs b/src/Blazor.Clipboard/ClipboardItem.cs new file mode 100644 index 00000000..aa6ac6e3 --- /dev/null +++ b/src/Blazor.Clipboard/ClipboardItem.cs @@ -0,0 +1,16 @@ +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.JSInterop; + +public class ClipboardItem +{ + public IReadOnlyList Types { get; private set; } = []; + + public async Task GetTypeAsync(string type) + { + // Implement your logic here to fetch Blob data based on type + // This is just a placeholder + return await Task.FromResult(Array.Empty()); + } +} \ No newline at end of file diff --git a/src/Blazor.Clipboard/ClipboardItems.cs b/src/Blazor.Clipboard/ClipboardItems.cs new file mode 100644 index 00000000..d58fc874 --- /dev/null +++ b/src/Blazor.Clipboard/ClipboardItems.cs @@ -0,0 +1,8 @@ +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.JSInterop; + +public class ClipboardItems : List +{ +} \ No newline at end of file diff --git a/src/Blazor.Clipboard/IClipboardService.cs b/src/Blazor.Clipboard/IClipboardService.cs new file mode 100644 index 00000000..e407ec27 --- /dev/null +++ b/src/Blazor.Clipboard/IClipboardService.cs @@ -0,0 +1,11 @@ +// Copyright (c) David Pine. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.JSInterop; + +[JSAutoInterop( + TypeName = "Clipboard", + Implementation = "window.navigator.clipboard", + HostingModel = BlazorHostingModel.Server, + Url = "https://developer.mozilla.org/docs/Web/API/Clipboard")] +public partial interface IClipboardService; \ No newline at end of file diff --git a/src/Blazor.Clipboard/README.md b/src/Blazor.Clipboard/README.md new file mode 100644 index 00000000..8b7f34d9 --- /dev/null +++ b/src/Blazor.Clipboard/README.md @@ -0,0 +1,93 @@ +# Blazorators: The Source Generated `geolocation` JavaScript Interop library for Blazor + +The [`Blazor.Geolocation`](https://www.nuget.org/packages/Blazor.Geolocation) package consumes the [`Blazor.SourceGenerators`](https://www.nuget.org/packages/Blazor.SourceGenerators) package. It exposes a source generated `IGeolocation` interface specific to Blazor WebAssembly and the [`geolocation`](https://developer.mozilla.org/docs/Web/API/Geolocation) Web API. + +## Get started + +After the NuGet package is added as a reference, call the `AddGeolocationServices` method to register the `IGeolocationService` service type. + +```csharp +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddGeolocationServices(); +builder.Services.AddRazorPages(); +builder.Services.AddServerSideBlazor(); + +var app = builder.Build(); + +app.UseHttpsRedirection(); +app.UseStaticFiles(); +app.UseRouting(); + +app.MapControllers(); +app.MapBlazorHub(); +app.MapFallbackToPage("/_Host"); + +app.Run(); +``` + +Anywhere needed within your Razor component, or Blazor client code — either `@inject` or `[Inject]` the `IGeolocationService` type. The interface takes the following shape: + +```csharp +#nullable enable +namespace Microsoft.JSInterop; + +/// +/// Source generated interface definition of the Geolocation type. +/// +public interface IGeolocationService +{ + /// + /// Source generated implementation of window.navigator.geolocation.clearWatch. + /// + /// + ValueTask ClearWatchAsync(double watchId); + + /// + /// Source generated implementation of window.navigator.geolocation.getCurrentPosition. + /// + /// + /// The calling Razor (or Blazor) component. + /// Expects the name of a + /// "JSInvokableAttribute" C# method with the following + /// System.Action{GeolocationPosition}". + /// Expects the name of a + /// "JSInvokableAttribute" C# method with the following + /// System.Action{GeolocationPositionError}". + /// The PositionOptions value. + ValueTask GetCurrentPositionAsync( + TComponent component, + string onSuccessCallbackMethodName, + string? onErrorCallbackMethodName = null, + PositionOptions? options = null) + where TComponent : class; + + /// + /// Source generated implementation of window.navigator.geolocation.watchPosition. + /// + /// + /// The calling Razor (or Blazor) component. + /// Expects the name of a + /// "JSInvokableAttribute" C# method with the following + /// System.Action{GeolocationPosition}". + /// Expects the name of a + /// "JSInvokableAttribute" C# method with the following + /// System.Action{GeolocationPositionError}". + /// The PositionOptions value. + ValueTask WatchPositionAsync( + TComponent component, + string onSuccessCallbackMethodName, + string? onErrorCallbackMethodName = null, + PositionOptions? options = null) + where TComponent : class; +} +``` + +### Add JavaScript dependency + +In the *_Host.cshtml* file, add the following: + +```html + +``` \ No newline at end of file From f176a108c8249c0e062a05728e43b512f353889e Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Fri, 19 Jul 2024 09:59:01 +0200 Subject: [PATCH 34/41] feat: add sample pages to show the basic clipboard --- .../Components/Layout/NavMenu.razor | 5 +++++ .../Components/Pages/CopyAndPaste.razor | 17 +++++++++++++++++ .../Components/Pages/CopyAndPaste.razor.cs | 19 +++++++++++++++++++ .../BlazorServer.ExampleConsumer.csproj | 6 ++++++ .../Pages/CopyAndPaste.razor | 17 +++++++++++++++++ .../Pages/CopyAndPaste.razor.cs | 19 +++++++++++++++++++ .../CSharp/CSharpTopLevelObject.cs | 6 +++--- 7 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 samples/Blazor.ExampleConsumer/Components/Pages/CopyAndPaste.razor create mode 100644 samples/Blazor.ExampleConsumer/Components/Pages/CopyAndPaste.razor.cs create mode 100644 samples/BlazorServer.ExampleConsumer/Pages/CopyAndPaste.razor create mode 100644 samples/BlazorServer.ExampleConsumer/Pages/CopyAndPaste.razor.cs diff --git a/samples/Blazor.ExampleConsumer/Components/Layout/NavMenu.razor b/samples/Blazor.ExampleConsumer/Components/Layout/NavMenu.razor index 3542a7ef..8d191260 100644 --- a/samples/Blazor.ExampleConsumer/Components/Layout/NavMenu.razor +++ b/samples/Blazor.ExampleConsumer/Components/Layout/NavMenu.razor @@ -42,6 +42,11 @@ Track +
- diff --git a/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor.cs b/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor.cs index e4be4437..06842409 100644 --- a/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor.cs +++ b/samples/BlazorServer.ExampleConsumer/Pages/ReadToMe.razor.cs @@ -10,7 +10,7 @@ public sealed partial class ReadToMe : IAsyncDisposable const string TextKey = "read-to-me-text"; string? _text = "Blazorators is an open-source project that strives to simplify JavaScript interop in Blazor. JavaScript interoperability is possible by parsing TypeScript type declarations and using this metadata to output corresponding C# types."; - SpeechSynthesisVoice[] _voices = Array.Empty(); + SpeechSynthesisVoice[] _voices = []; readonly IList _voiceSpeeds = Enumerable.Range(0, 12).Select(i => (i + 1) * .25).ToList(); double _voiceSpeed = 1.5; @@ -23,7 +23,7 @@ public sealed partial class ReadToMe : IAsyncDisposable Rate = _voiceSpeed, Volume = 1, Voice = _selectedVoice is { Length: > 0 } - ? _voices?.FirstOrDefault(voice => voice.Name == _selectedVoice) + ? Array.Find(_voices, voice => voice.Name == _selectedVoice) : null }; @@ -41,35 +41,35 @@ public sealed partial class ReadToMe : IAsyncDisposable protected override async Task OnAfterRenderAsync(bool firstRender) { - if (firstRender is false) + if (firstRender) { - return; - } - - await GetVoicesAsync(); - - if (await LocalStorage.GetItemAsync(PreferredVoiceKey) - is { Length: > 0 } voice) - { - _selectedVoice = voice; - } - if (await LocalStorage.GetItemAsync(PreferredSpeedKey) - is { Length: > 0 } s && - double.TryParse(s, out var speed) && speed > 0) - { - _voiceSpeed = speed; - } - if (await SessionStorage.GetItemAsync(TextKey) - is { Length: > 0 } text) - { - _text = text; + await GetVoicesAsync(); + + if (await LocalStorage.GetItemAsync(PreferredVoiceKey) + is { Length: > 0 } voice) + { + _selectedVoice = voice; + } + if (await LocalStorage.GetItemAsync(PreferredSpeedKey) + is { Length: > 0 } s && + double.TryParse(s, out var speed) && speed > 0) + { + _voiceSpeed = speed; + } + if (await SessionStorage.GetItemAsync(TextKey) + is { Length: > 0 } text) + { + _text = text; + } + + await InvokeAsync(StateHasChanged); } } async Task GetVoicesAsync(bool isFromCallback = false) { _voices = await SpeechSynthesis.GetVoicesAsync(); - if (_voices is { } && isFromCallback) + if (_voices is not null && isFromCallback) { StateHasChanged(); } @@ -77,10 +77,6 @@ async Task GetVoicesAsync(bool isFromCallback = false) void OnTextChanged(ChangeEventArgs args) => _text = args.Value?.ToString(); - void OnVoiceSpeedChange(ChangeEventArgs args) => - _voiceSpeed = double.TryParse(args.Value?.ToString() ?? "1.5", out var speed) - ? speed : 1.5; - ValueTask Speak() => SpeechSynthesis.SpeakAsync(Utterance); async ValueTask IAsyncDisposable.DisposeAsync() diff --git a/samples/BlazorServer.ExampleConsumer/Shared/NavMenu.razor b/samples/BlazorServer.ExampleConsumer/Shared/NavMenu.razor index 3542a7ef..8d191260 100644 --- a/samples/BlazorServer.ExampleConsumer/Shared/NavMenu.razor +++ b/samples/BlazorServer.ExampleConsumer/Shared/NavMenu.razor @@ -42,6 +42,11 @@ Track
+
public static class SpeechSynthesisServiceExtensions { - readonly static ConcurrentDictionary> s_callbackRegistry = new(); - readonly static ConcurrentDictionary> s_utteranceEndedCallbackRegistry = new(); + private static readonly ConcurrentDictionary> s_callbackRegistry = new(); + private static readonly ConcurrentDictionary> s_utteranceEndedCallbackRegistry = new(); /// /// This extension wraps the @@ -27,7 +27,7 @@ public static void Speak( SpeechSynthesisUtterance utterance, Action onUtteranceEnded) { - if (service is SpeechSynthesisService and { _javaScript: { } } svc) + if (service is SpeechSynthesisService and { _javaScript: { } }) { s_utteranceEndedCallbackRegistry[utterance.Text] = onUtteranceEnded; service.Speak(utterance); @@ -132,4 +132,4 @@ public static void UnsubscribeFromVoicesChanged( "SpeechSynthesisService is not available."); } } -} +} \ No newline at end of file From 4cf63b7153baca94c98a331018b1059de38bc854 Mon Sep 17 00:00:00 2001 From: Denny09310 Date: Tue, 23 Jul 2024 20:02:31 +0200 Subject: [PATCH 39/41] fix: remove global invariant culture in favor of scoped one --- .../Blazor.ExampleConsumer.csproj | 1 - .../Components/Pages/ReadToMe.razor | 23 ++++++++++--------- .../BlazorServer.ExampleConsumer.csproj | 1 - .../Pages/ReadToMe.razor | 3 ++- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj b/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj index c600dfb2..3f778478 100644 --- a/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj +++ b/samples/Blazor.ExampleConsumer/Blazor.ExampleConsumer.csproj @@ -4,7 +4,6 @@ net8.0 enable enable - true diff --git a/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor b/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor index 9d46133b..7683cb23 100644 --- a/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor +++ b/samples/Blazor.ExampleConsumer/Components/Pages/ReadToMe.razor @@ -6,14 +6,14 @@
-
@@ -21,13 +21,14 @@
+ step=".25" class="form-range" id="range" list="speeds" + @bind="@_voiceSpeed" + @bind:culture="System.Globalization.CultureInfo.InvariantCulture"> - @foreach (var speed in _voiceSpeeds) - { - - } + @foreach (var speed in _voiceSpeeds) + { + + }
@@ -36,7 +37,7 @@ Text-to-speech @(_elapsedTimeMessage is { Length: > 0 } ? $"({_elapsedTimeMessage})" : "") @@ -48,7 +49,7 @@ -