feat: initial release
This commit is contained in:
commit
78054ee2a7
164 changed files with 22055 additions and 0 deletions
122
.editorconfig
Normal file
122
.editorconfig
Normal file
|
@ -0,0 +1,122 @@
|
|||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset=utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline=true
|
||||
indent_style=space
|
||||
indent_size=4
|
||||
|
||||
# Microsoft .NET properties
|
||||
csharp_new_line_before_members_in_object_initializers=false
|
||||
csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
|
||||
csharp_space_after_cast=false
|
||||
csharp_style_var_elsewhere=false:hint
|
||||
csharp_style_var_for_built_in_types=false:hint
|
||||
csharp_style_var_when_type_is_apparent=true:hint
|
||||
csharp_preserve_single_line_statements=false
|
||||
csharp_preserve_single_line_blocks=true
|
||||
dotnet_style_predefined_type_for_locals_parameters_members=true:hint
|
||||
dotnet_style_predefined_type_for_member_access=true:hint
|
||||
dotnet_style_qualification_for_event=true:hint
|
||||
dotnet_style_qualification_for_field=true:hint
|
||||
dotnet_style_qualification_for_method=true:hint
|
||||
dotnet_style_qualification_for_property=true:hint
|
||||
dotnet_style_require_accessibility_modifiers=for_non_interface_members:hint
|
||||
|
||||
# ReSharper properties
|
||||
resharper_alignment_tab_fill_style=optimal_fill
|
||||
resharper_apply_on_completion=true
|
||||
resharper_blank_lines_after_control_transfer_statements=1
|
||||
resharper_blank_lines_around_single_line_auto_property=1
|
||||
resharper_blank_lines_around_single_line_property=1
|
||||
resharper_blank_lines_before_single_line_comment=1
|
||||
resharper_blank_lines_between_using_groups=1
|
||||
resharper_braces_for_for=required
|
||||
resharper_braces_for_foreach=required
|
||||
resharper_braces_for_ifelse=required
|
||||
resharper_braces_for_while=required
|
||||
resharper_can_use_global_alias=false
|
||||
resharper_csharp_blank_lines_around_single_line_field=1
|
||||
resharper_csharp_blank_lines_around_single_line_invocable=1
|
||||
resharper_csharp_indent_style=tab
|
||||
resharper_csharp_insert_final_newline=true
|
||||
resharper_csharp_keep_blank_lines_in_code=1
|
||||
resharper_csharp_keep_blank_lines_in_declarations=1
|
||||
resharper_csharp_new_line_before_while=true
|
||||
resharper_csharp_use_indent_from_vs=false
|
||||
resharper_csharp_wrap_arguments_style=chop_if_long
|
||||
resharper_csharp_wrap_extends_list_style=chop_if_long
|
||||
resharper_csharp_wrap_parameters_style=chop_if_long
|
||||
resharper_css_insert_final_newline=false
|
||||
resharper_enforce_line_ending_style=true
|
||||
resharper_html_insert_final_newline=false
|
||||
resharper_indent_nested_fixed_stmt=true
|
||||
resharper_js_indent_style=tab
|
||||
resharper_js_insert_final_newline=true
|
||||
resharper_js_keep_blank_lines_in_code=1
|
||||
resharper_js_stick_comment=false
|
||||
resharper_js_use_indent_from_vs=false
|
||||
resharper_js_wrap_before_binary_opsign=true
|
||||
resharper_js_wrap_chained_method_calls=chop_if_long
|
||||
resharper_keep_blank_lines_between_declarations=1
|
||||
resharper_min_blank_lines_after_imports=1
|
||||
resharper_place_attribute_on_same_line=False
|
||||
resharper_place_constructor_initializer_on_same_line=false
|
||||
resharper_place_type_constraints_on_same_line=false
|
||||
resharper_protobuf_insert_final_newline=false
|
||||
resharper_qualified_using_at_nested_scope=true
|
||||
resharper_resx_insert_final_newline=false
|
||||
resharper_space_within_single_line_array_initializer_braces=true
|
||||
resharper_use_indents_from_main_language_in_file=false
|
||||
resharper_vb_insert_final_newline=false
|
||||
resharper_wrap_after_declaration_lpar=true
|
||||
resharper_wrap_after_invocation_lpar=true
|
||||
resharper_wrap_before_extends_colon=true
|
||||
resharper_wrap_before_first_type_parameter_constraint=true
|
||||
resharper_wrap_before_type_parameter_langle=true
|
||||
resharper_xmldoc_indent_child_elements=ZeroIndent
|
||||
resharper_xmldoc_indent_text=ZeroIndent
|
||||
resharper_xmldoc_insert_final_newline=false
|
||||
resharper_xml_insert_final_newline=false
|
||||
|
||||
# ReSharper inspection severities
|
||||
resharper_check_namespace_highlighting=none
|
||||
resharper_convert_to_auto_property_highlighting=none
|
||||
resharper_localizable_element_highlighting=none
|
||||
resharper_redundant_comma_in_attribute_list_highlighting=none
|
||||
resharper_redundant_comma_in_enum_declaration_highlighting=none
|
||||
resharper_redundant_comma_in_initializer_highlighting=none
|
||||
resharper_string_compare_to_is_culture_specific_highlighting=none
|
||||
resharper_string_index_of_is_culture_specific_1_highlighting=none
|
||||
resharper_use_null_propagation_highlighting=none
|
||||
resharper_use_object_or_collection_initializer_highlighting=hint
|
||||
resharper_use_string_interpolation_highlighting=hint
|
||||
|
||||
# Matches the exact files either package.json or .travis.yml
|
||||
[{package.json,.travis.yml}]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
tab_width=2
|
||||
|
||||
[*.{cs,js,json,jsx,proto,resjson,ts,tsx}]
|
||||
indent_style=space
|
||||
indent_size=space
|
||||
tab_width=4
|
||||
|
||||
[*.{asax,ascx,aspx,cshtml,css,htm,html,master,razor,skin,vb,xaml,xamlx,xoml}]
|
||||
indent_style=space
|
||||
indent_size=4
|
||||
tab_width=4
|
||||
|
||||
[*.{appxmanifest,build,config,csproj,dbml,discomap,dtd,jsproj,lsproj,njsproj,nuspec,proj,props,resw,resx,StyleCop,targets,tasks,vbproj,xml,xsd}]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
tab_width=2
|
||||
|
||||
[*.proto]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
tab_width=2
|
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
|||
use asdf
|
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
launchSettings.json
|
||||
|
||||
*~
|
||||
*.user
|
||||
Directory.Build.props
|
||||
|
||||
obj/
|
||||
[Bb]in/
|
||||
.vs/
|
||||
.vscode/
|
||||
.idea/
|
||||
_ReSharper.Caches/
|
||||
node_modules/
|
51
.gitlab-ci.yml
Normal file
51
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,51 @@
|
|||
stages:
|
||||
- build
|
||||
|
||||
default:
|
||||
before_script:
|
||||
- curl -sL https://deb.nodesource.com/setup_15.x | bash -
|
||||
- apt-get install -y nodejs
|
||||
|
||||
build:
|
||||
image: mcr.microsoft.com/dotnet/sdk:5.0
|
||||
stage: build
|
||||
script:
|
||||
# Set up the environment.
|
||||
- npx npm install --ci
|
||||
- npx commitlint-gitlab-ci -x @commitlint/config-conventional
|
||||
|
||||
# Build and test everything.
|
||||
- dotnet restore
|
||||
- dotnet build
|
||||
#- 'dotnet test --test-adapter-path:. --logger:"junit;LogFilePath=../artifacts/{assembly}-test-result.xml;MethodFormat=Default;FailureBodyFormat=Verbose" --collect:"XPlat Code Coverage"'
|
||||
|
||||
# Summarize the output for Gitlab CI reporting.
|
||||
#- dotnet new tool-manifest
|
||||
#- dotnet tool install dotnet-reportgenerator-globaltool
|
||||
#- dotnet tool run reportgenerator -reports:src/*/TestResults/*/coverage.cobertura.xml -targetdir:./coverage "-reporttypes:Cobertura;TextSummary"
|
||||
#- grep "Line coverage" coverage/Summary.txt
|
||||
|
||||
# Perform the release.
|
||||
- npx semantic-release
|
||||
|
||||
rules:
|
||||
- if: '$CI_COMMIT_TITLE =~ /^chore\(release\)/'
|
||||
when: never
|
||||
- if: '$CI_COMMIT_TAG'
|
||||
when: never
|
||||
- if: '$CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH'
|
||||
when: never
|
||||
- when: on_success
|
||||
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- ./**/*test-result.xml
|
||||
- ./coverage/Cobertura.xml
|
||||
- ./coverage/Summary.*
|
||||
- ./**/*.nupkg
|
||||
reports:
|
||||
junit:
|
||||
- ./**/*test-result.xml
|
||||
cobertura:
|
||||
- ./coverage/Cobertura.xml
|
4
.husky/commit-msg
Executable file
4
.husky/commit-msg
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install commitlint --edit $1
|
3
.tool-versions
Normal file
3
.tool-versions
Normal file
|
@ -0,0 +1,3 @@
|
|||
dotnet-core 5.0.100
|
||||
yarn 1.22.10
|
||||
nodejs 15.0.1
|
21
LICENSE.txt
Normal file
21
LICENSE.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) Moonfire Games
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
264
Nitride.sln
Normal file
264
Nitride.sln
Normal file
|
@ -0,0 +1,264 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26124.0
|
||||
MinimumVisualStudioVersion = 15.0.26124.0
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{570184FD-ECE4-4EC8-86E1-C1265E17D647}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Calendar", "src\Nitride.Calendar\Nitride.Calendar.csproj", "{D480943C-764D-4A8A-B546-642ED10586BB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Feeds", "src\Nitride.Feeds\Nitride.Feeds.csproj", "{1204DECC-654A-433A-BF82-53F98AB24DCF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Gemtext", "src\Nitride.Gemtext\Nitride.Gemtext.csproj", "{23C7CBF7-9624-457A-8296-C03F75BC9BC6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Generators", "src\Nitride.Generators\Nitride.Generators.csproj", "{4ACB11B7-1EEB-48E7-845A-528770839125}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Handlebars", "src\Nitride.Handlebars\Nitride.Handlebars.csproj", "{56E595A6-7880-416E-B328-93B9617F92A6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Html", "src\Nitride.Html\Nitride.Html.csproj", "{3F9292D3-DA50-4DBA-AE90-E33E462470A1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.IO", "src\Nitride.IO\Nitride.IO.csproj", "{534BF940-25B2-4948-A101-7890CC9C4EA5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.IO.Tests", "src\Nitride.IO.Tests\Nitride.IO.Tests.csproj", "{57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Javascript", "src\Nitride.Javascript\Nitride.Javascript.csproj", "{285A855E-B2FC-4770-AFD9-4CC67AD3C83C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Markdown", "src\Nitride.Markdown\Nitride.Markdown.csproj", "{41FF3823-7008-43B1-AD6A-92437E0600B7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride", "src\Nitride\Nitride.csproj", "{757BA115-3465-46C5-ADDB-7B96D6900F33}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Slugs", "src\Nitride.Slugs\Nitride.Slugs.csproj", "{17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Temporal", "src\Nitride.Temporal\Nitride.Temporal.csproj", "{FEFFA469-245E-45A7-A094-3F0E89CEF3A9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Tests", "src\Nitride.Tests\Nitride.Tests.csproj", "{A0EED899-35C7-4C1C-8BBF-F7FD2F281839}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Yaml", "src\Nitride.Yaml\Nitride.Yaml.csproj", "{1A5B4B4D-CF32-4458-8F2C-83A2AE494837}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Yaml.Tests", "src\Nitride.Yaml.Tests\Nitride.Yaml.Tests.csproj", "{C8CD60C2-2A26-48AE-848D-A71AEE2267C9}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D480943C-764D-4A8A-B546-642ED10586BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D480943C-764D-4A8A-B546-642ED10586BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D480943C-764D-4A8A-B546-642ED10586BB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{D480943C-764D-4A8A-B546-642ED10586BB}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{D480943C-764D-4A8A-B546-642ED10586BB}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{D480943C-764D-4A8A-B546-642ED10586BB}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{D480943C-764D-4A8A-B546-642ED10586BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D480943C-764D-4A8A-B546-642ED10586BB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D480943C-764D-4A8A-B546-642ED10586BB}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{D480943C-764D-4A8A-B546-642ED10586BB}.Release|x64.Build.0 = Release|Any CPU
|
||||
{D480943C-764D-4A8A-B546-642ED10586BB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{D480943C-764D-4A8A-B546-642ED10586BB}.Release|x86.Build.0 = Release|Any CPU
|
||||
{1204DECC-654A-433A-BF82-53F98AB24DCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1204DECC-654A-433A-BF82-53F98AB24DCF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1204DECC-654A-433A-BF82-53F98AB24DCF}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{1204DECC-654A-433A-BF82-53F98AB24DCF}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{1204DECC-654A-433A-BF82-53F98AB24DCF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{1204DECC-654A-433A-BF82-53F98AB24DCF}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{1204DECC-654A-433A-BF82-53F98AB24DCF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1204DECC-654A-433A-BF82-53F98AB24DCF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1204DECC-654A-433A-BF82-53F98AB24DCF}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{1204DECC-654A-433A-BF82-53F98AB24DCF}.Release|x64.Build.0 = Release|Any CPU
|
||||
{1204DECC-654A-433A-BF82-53F98AB24DCF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{1204DECC-654A-433A-BF82-53F98AB24DCF}.Release|x86.Build.0 = Release|Any CPU
|
||||
{23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4ACB11B7-1EEB-48E7-845A-528770839125}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4ACB11B7-1EEB-48E7-845A-528770839125}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4ACB11B7-1EEB-48E7-845A-528770839125}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4ACB11B7-1EEB-48E7-845A-528770839125}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4ACB11B7-1EEB-48E7-845A-528770839125}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4ACB11B7-1EEB-48E7-845A-528770839125}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4ACB11B7-1EEB-48E7-845A-528770839125}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4ACB11B7-1EEB-48E7-845A-528770839125}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4ACB11B7-1EEB-48E7-845A-528770839125}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4ACB11B7-1EEB-48E7-845A-528770839125}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4ACB11B7-1EEB-48E7-845A-528770839125}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4ACB11B7-1EEB-48E7-845A-528770839125}.Release|x86.Build.0 = Release|Any CPU
|
||||
{56E595A6-7880-416E-B328-93B9617F92A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{56E595A6-7880-416E-B328-93B9617F92A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{56E595A6-7880-416E-B328-93B9617F92A6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{56E595A6-7880-416E-B328-93B9617F92A6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{56E595A6-7880-416E-B328-93B9617F92A6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{56E595A6-7880-416E-B328-93B9617F92A6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{56E595A6-7880-416E-B328-93B9617F92A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{56E595A6-7880-416E-B328-93B9617F92A6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{56E595A6-7880-416E-B328-93B9617F92A6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{56E595A6-7880-416E-B328-93B9617F92A6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{56E595A6-7880-416E-B328-93B9617F92A6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{56E595A6-7880-416E-B328-93B9617F92A6}.Release|x86.Build.0 = Release|Any CPU
|
||||
{3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Release|x86.Build.0 = Release|Any CPU
|
||||
{534BF940-25B2-4948-A101-7890CC9C4EA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{534BF940-25B2-4948-A101-7890CC9C4EA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{534BF940-25B2-4948-A101-7890CC9C4EA5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{534BF940-25B2-4948-A101-7890CC9C4EA5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{534BF940-25B2-4948-A101-7890CC9C4EA5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{534BF940-25B2-4948-A101-7890CC9C4EA5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{534BF940-25B2-4948-A101-7890CC9C4EA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{534BF940-25B2-4948-A101-7890CC9C4EA5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{534BF940-25B2-4948-A101-7890CC9C4EA5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{534BF940-25B2-4948-A101-7890CC9C4EA5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{534BF940-25B2-4948-A101-7890CC9C4EA5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{534BF940-25B2-4948-A101-7890CC9C4EA5}.Release|x86.Build.0 = Release|Any CPU
|
||||
{57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Release|x64.Build.0 = Release|Any CPU
|
||||
{57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Release|x86.Build.0 = Release|Any CPU
|
||||
{285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Release|x86.Build.0 = Release|Any CPU
|
||||
{41FF3823-7008-43B1-AD6A-92437E0600B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{41FF3823-7008-43B1-AD6A-92437E0600B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{41FF3823-7008-43B1-AD6A-92437E0600B7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{41FF3823-7008-43B1-AD6A-92437E0600B7}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{41FF3823-7008-43B1-AD6A-92437E0600B7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{41FF3823-7008-43B1-AD6A-92437E0600B7}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{41FF3823-7008-43B1-AD6A-92437E0600B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{41FF3823-7008-43B1-AD6A-92437E0600B7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{41FF3823-7008-43B1-AD6A-92437E0600B7}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{41FF3823-7008-43B1-AD6A-92437E0600B7}.Release|x64.Build.0 = Release|Any CPU
|
||||
{41FF3823-7008-43B1-AD6A-92437E0600B7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{41FF3823-7008-43B1-AD6A-92437E0600B7}.Release|x86.Build.0 = Release|Any CPU
|
||||
{757BA115-3465-46C5-ADDB-7B96D6900F33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{757BA115-3465-46C5-ADDB-7B96D6900F33}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{757BA115-3465-46C5-ADDB-7B96D6900F33}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{757BA115-3465-46C5-ADDB-7B96D6900F33}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{757BA115-3465-46C5-ADDB-7B96D6900F33}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{757BA115-3465-46C5-ADDB-7B96D6900F33}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{757BA115-3465-46C5-ADDB-7B96D6900F33}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{757BA115-3465-46C5-ADDB-7B96D6900F33}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{757BA115-3465-46C5-ADDB-7B96D6900F33}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{757BA115-3465-46C5-ADDB-7B96D6900F33}.Release|x64.Build.0 = Release|Any CPU
|
||||
{757BA115-3465-46C5-ADDB-7B96D6900F33}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{757BA115-3465-46C5-ADDB-7B96D6900F33}.Release|x86.Build.0 = Release|Any CPU
|
||||
{17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Release|x86.Build.0 = Release|Any CPU
|
||||
{FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Release|x64.Build.0 = Release|Any CPU
|
||||
{FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Release|x86.Build.0 = Release|Any CPU
|
||||
{1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Release|x64.Build.0 = Release|Any CPU
|
||||
{1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{D480943C-764D-4A8A-B546-642ED10586BB} = {570184FD-ECE4-4EC8-86E1-C1265E17D647}
|
||||
{1204DECC-654A-433A-BF82-53F98AB24DCF} = {570184FD-ECE4-4EC8-86E1-C1265E17D647}
|
||||
{23C7CBF7-9624-457A-8296-C03F75BC9BC6} = {570184FD-ECE4-4EC8-86E1-C1265E17D647}
|
||||
{4ACB11B7-1EEB-48E7-845A-528770839125} = {570184FD-ECE4-4EC8-86E1-C1265E17D647}
|
||||
{56E595A6-7880-416E-B328-93B9617F92A6} = {570184FD-ECE4-4EC8-86E1-C1265E17D647}
|
||||
{3F9292D3-DA50-4DBA-AE90-E33E462470A1} = {570184FD-ECE4-4EC8-86E1-C1265E17D647}
|
||||
{534BF940-25B2-4948-A101-7890CC9C4EA5} = {570184FD-ECE4-4EC8-86E1-C1265E17D647}
|
||||
{57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D} = {570184FD-ECE4-4EC8-86E1-C1265E17D647}
|
||||
{285A855E-B2FC-4770-AFD9-4CC67AD3C83C} = {570184FD-ECE4-4EC8-86E1-C1265E17D647}
|
||||
{41FF3823-7008-43B1-AD6A-92437E0600B7} = {570184FD-ECE4-4EC8-86E1-C1265E17D647}
|
||||
{757BA115-3465-46C5-ADDB-7B96D6900F33} = {570184FD-ECE4-4EC8-86E1-C1265E17D647}
|
||||
{17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5} = {570184FD-ECE4-4EC8-86E1-C1265E17D647}
|
||||
{FEFFA469-245E-45A7-A094-3F0E89CEF3A9} = {570184FD-ECE4-4EC8-86E1-C1265E17D647}
|
||||
{A0EED899-35C7-4C1C-8BBF-F7FD2F281839} = {570184FD-ECE4-4EC8-86E1-C1265E17D647}
|
||||
{1A5B4B4D-CF32-4458-8F2C-83A2AE494837} = {570184FD-ECE4-4EC8-86E1-C1265E17D647}
|
||||
{C8CD60C2-2A26-48AE-848D-A71AEE2267C9} = {570184FD-ECE4-4EC8-86E1-C1265E17D647}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
3
Nitride.sln.DotSettings
Normal file
3
Nitride.sln.DotSettings
Normal file
|
@ -0,0 +1,3 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Gemtext/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Markdig/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
1370
Nitride.sln.Dotsettings
Normal file
1370
Nitride.sln.Dotsettings
Normal file
File diff suppressed because it is too large
Load diff
7
NuGet.Config
Normal file
7
NuGet.Config
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||
<add key="mfgames" value="https://www.myget.org/F/mfgames/api/v3/index.json" protocolVersion="3" />
|
||||
</packageSources>
|
||||
</configuration>
|
4
README.md
Normal file
4
README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
Nitride CIL
|
||||
===========
|
||||
|
||||
A static site generator based on the [Gallium ECS](https://gitlab.com/mfgames-cil/gallium-cil/).
|
3
commitlint.config.js
Normal file
3
commitlint.config.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
extends: ["@commitlint/config-conventional"],
|
||||
};
|
12304
package-lock.json
generated
Normal file
12304
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
21
package.json
Normal file
21
package.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "nitride-cil",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^13.1.0",
|
||||
"@commitlint/config-conventional": "^13.1.0",
|
||||
"@semantic-release/changelog": "^5.0.1",
|
||||
"@semantic-release/git": "^9.0.0",
|
||||
"@semantic-release/gitlab": "^6.2.2",
|
||||
"@semantic-release/npm": "^7.1.3",
|
||||
"commitlint-gitlab-ci": "^0.0.4",
|
||||
"husky": "^7.0.2",
|
||||
"semantic-release": "^17.4.7",
|
||||
"semantic-release-dotnet": "^1.0.0",
|
||||
"semantic-release-nuget": "^1.1.0"
|
||||
}
|
||||
}
|
20
release.config.js
Normal file
20
release.config.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
module.exports = {
|
||||
branches: ["main"],
|
||||
message: "chore(release): v${nextRelease.version}\n\n${nextRelease.notes}",
|
||||
plugins: [
|
||||
"@semantic-release/commit-analyzer",
|
||||
"@semantic-release/release-notes-generator",
|
||||
"@semantic-release/npm",
|
||||
"semantic-release-dotnet",
|
||||
[
|
||||
"semantic-release-nuget",
|
||||
{
|
||||
packArguments: ["--include-symbols", "--include-source"],
|
||||
pushFiles: ["src/*/bin/Debug/*.nupkg"],
|
||||
},
|
||||
],
|
||||
"@semantic-release/changelog",
|
||||
"@semantic-release/git",
|
||||
"@semantic-release/gitlab",
|
||||
],
|
||||
};
|
14
src/Directory.Build.props
Normal file
14
src/Directory.Build.props
Normal file
|
@ -0,0 +1,14 @@
|
|||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Authors>Dylan Moonfire</Authors>
|
||||
<Company>Moonfire Games</Company>
|
||||
<RepositoryUrl>https://gitlab.com/mfgames-cil/nitride-cil</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<PackageTags>nitride</PackageTags>
|
||||
<PackageProjectUrl>https://gitlab.com/mfgames-cil/nitride-cil</PackageProjectUrl>
|
||||
<PackageLicense>MIT</PackageLicense>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
104
src/Nitride.Calendar/CreateCalender.cs
Normal file
104
src/Nitride.Calendar/CreateCalender.cs
Normal file
|
@ -0,0 +1,104 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Gallium;
|
||||
using Ical.Net.CalendarComponents;
|
||||
using Ical.Net.DataTypes;
|
||||
using Ical.Net.Serialization;
|
||||
using Nitride.Contents;
|
||||
using Nitride.Temporal;
|
||||
using NodaTime;
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.Calendar
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an iCalendar file from all the entities passed into the method
|
||||
/// that have a NodaTime.Instant component. This will write both past and
|
||||
/// future events.
|
||||
/// </summary>
|
||||
[WithProperties]
|
||||
public partial class CreateCalender : NitrideOperationBase
|
||||
{
|
||||
private readonly NitrideClock clock;
|
||||
|
||||
public CreateCalender(NitrideClock clock)
|
||||
{
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a callback to get the summary of the event representing
|
||||
/// the entity.
|
||||
/// </summary>
|
||||
public Func<Entity, string>? GetEventSummary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a callback to get the optional URL of an event for
|
||||
/// the entity.
|
||||
/// </summary>
|
||||
public Func<Entity, Uri?>? GetEventUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the file system path for the resulting calendar.
|
||||
/// </summary>
|
||||
public UPath? Path { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
this.CheckNotNull(x => x.Path);
|
||||
this.CheckNotNull(x => x.GetEventSummary);
|
||||
|
||||
IEnumerable<Entity> output = input
|
||||
.ForEntities<Instant>(this.CreateCalendarEntity);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private IEnumerable<Entity> CreateCalendarEntity(
|
||||
IEnumerable<Entity> entities)
|
||||
{
|
||||
// Create the calendar in the same time zone as the rest of the system.
|
||||
var calendar = new Ical.Net.Calendar();
|
||||
|
||||
calendar.TimeZones.Add(new VTimeZone(this.clock.DateTimeZone.Id));
|
||||
|
||||
// Go through the events and add all of them.
|
||||
List<Entity> input = entities.ToList();
|
||||
IEnumerable<CalendarEvent> events =
|
||||
input.Select(this.CreateCalendarEvent);
|
||||
|
||||
calendar.Events.AddRange(events);
|
||||
|
||||
// Create the iCalendar file.
|
||||
var serializer = new CalendarSerializer();
|
||||
string serializedCalendar = serializer.SerializeToString(calendar);
|
||||
|
||||
// Create the calendar entity and populate everything.
|
||||
Entity calendarEntity = new Entity()
|
||||
.Set(IsCalendar.Instance)
|
||||
.Set(this.Path!.Value)
|
||||
.SetTextContent(serializedCalendar);
|
||||
|
||||
// Return the results along with the new calendar.
|
||||
return input.Union(new[] { calendarEntity });
|
||||
}
|
||||
|
||||
private CalendarEvent CreateCalendarEvent(Entity entity)
|
||||
{
|
||||
var instant = entity.Get<Instant>();
|
||||
var when = this.clock.ToDateTime(instant);
|
||||
string summary = this.GetEventSummary!(entity);
|
||||
Uri? url = this.GetEventUrl?.Invoke(entity);
|
||||
var calendarEvent = new CalendarEvent
|
||||
{
|
||||
Summary = summary,
|
||||
Start = new CalDateTime(when),
|
||||
Url = url,
|
||||
};
|
||||
|
||||
return calendarEvent;
|
||||
}
|
||||
}
|
||||
}
|
10
src/Nitride.Calendar/IsCalendar.cs
Normal file
10
src/Nitride.Calendar/IsCalendar.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Nitride.Calendar
|
||||
{
|
||||
/// <summary>
|
||||
/// A marker component for identifying an entity that represents a calendar.
|
||||
/// </summary>
|
||||
public class IsCalendar
|
||||
{
|
||||
public static IsCalendar Instance { get; } = new();
|
||||
}
|
||||
}
|
37
src/Nitride.Calendar/Nitride.Calendar.csproj
Normal file
37
src/Nitride.Calendar/Nitride.Calendar.csproj
Normal file
|
@ -0,0 +1,37 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>An extension to Nitride static site generator to generate iCalendar files.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride.IO\Nitride.IO.csproj" />
|
||||
<ProjectReference Include="..\Nitride.Temporal\Nitride.Temporal.csproj" />
|
||||
<ProjectReference Include="..\Nitride\Nitride.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Gallium" Version="1.0.2" />
|
||||
<PackageReference Include="Ical.Net" Version="4.2.0" />
|
||||
<PackageReference Include="NodaTime" Version="3.0.5" />
|
||||
<PackageReference Include="Zio" Version="0.12.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Include the source generator -->
|
||||
<PropertyGroup>
|
||||
<EmitCompilerGeneratedFiles>True</EmitCompilerGeneratedFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride.Generators\Nitride.Generators.csproj">
|
||||
<OutputItemType>Analyzer</OutputItemType>
|
||||
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
16
src/Nitride.Calendar/NitrideCalendarBuilderExtensions.cs
Normal file
16
src/Nitride.Calendar/NitrideCalendarBuilderExtensions.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using Autofac;
|
||||
using Nitride.Temporal;
|
||||
|
||||
namespace Nitride.Calendar
|
||||
{
|
||||
public static class NitrideCalendarBuilderExtensions
|
||||
{
|
||||
public static NitrideBuilder UseCalendar(this NitrideBuilder builder)
|
||||
{
|
||||
return builder
|
||||
.UseTemporal()
|
||||
.ConfigureContainer(
|
||||
x => x.RegisterModule<NitrideCalendarModule>());
|
||||
}
|
||||
}
|
||||
}
|
6
src/Nitride.Calendar/NitrideCalendarModule.cs
Normal file
6
src/Nitride.Calendar/NitrideCalendarModule.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Nitride.Calendar
|
||||
{
|
||||
public class NitrideCalendarModule : NitrideModuleBase
|
||||
{
|
||||
}
|
||||
}
|
123
src/Nitride.Feeds/CreateAtomFeeds.cs
Normal file
123
src/Nitride.Feeds/CreateAtomFeeds.cs
Normal file
|
@ -0,0 +1,123 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Gallium;
|
||||
using Nitride.Contents;
|
||||
using Nitride.Feeds.Structure;
|
||||
using NodaTime;
|
||||
using Serilog;
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.Feeds
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates various feeds from the given input.
|
||||
/// </summary>
|
||||
[WithProperties]
|
||||
public partial class CreateAtomFeeds : NitrideOperationBase
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
|
||||
public CreateAtomFeeds(ILogger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.GetAlternateMimeType = _ => "text/html";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the base URL for all the links.
|
||||
/// </summary>
|
||||
public string? BaseUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alternate MIME type.
|
||||
/// </summary>
|
||||
public Func<Entity, string> GetAlternateMimeType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alternate URL associated with the feed.
|
||||
/// </summary>
|
||||
public Func<Entity, Uri>? GetAlternateUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the callback to get the author for the feed.
|
||||
/// </summary>
|
||||
public Func<Entity, AtomAuthor>? GetAuthor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the callback to get the entries associated with the
|
||||
/// feed.
|
||||
/// </summary>
|
||||
public Func<Entity, IEnumerable<AtomEntry>>? GetEntries { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier (typically a URL) of the feed.
|
||||
/// </summary>
|
||||
public Func<Entity, string>? GetId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the callback to get the path of the generated feed.
|
||||
/// </summary>
|
||||
public Func<Entity, UPath>? GetPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rights (license) of the feed.
|
||||
/// </summary>
|
||||
public Func<Entity, string>? GetRights { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A callback that gets the title of the feed from the given entity.
|
||||
/// </summary>
|
||||
public Func<Entity, string>? GetTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the updated timestamp for the feed.
|
||||
/// </summary>
|
||||
public Func<Entity, Instant>? GetUpdated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the URL associated with the feed.
|
||||
/// </summary>
|
||||
public Func<Entity, Uri>? GetUrl { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
this.CheckNotNull(x => this.GetTitle);
|
||||
this.CheckNotNull(x => this.GetPath);
|
||||
this.CheckNotNull(x => this.GetEntries);
|
||||
|
||||
return input.SelectMany(this.CreateEntityFeed);
|
||||
}
|
||||
|
||||
private IEnumerable<Entity> CreateEntityFeed(Entity entity)
|
||||
{
|
||||
// Create the top-level feed. All the nullable callbacks were
|
||||
// verified in the function that calls this.
|
||||
var feed = new AtomFeed()
|
||||
.WithTitle(this.GetTitle?.Invoke(entity))
|
||||
.WithId(this.GetId?.Invoke(entity))
|
||||
.WithRights(this.GetRights?.Invoke(entity))
|
||||
.WithUpdated(this.GetUpdated?.Invoke(entity))
|
||||
.WithUrl(this.GetUrl?.Invoke(entity))
|
||||
.WithAlternateUrl(this.GetAlternateUrl?.Invoke(entity))
|
||||
.WithAlternateMimeType(this.GetAlternateMimeType.Invoke(entity))
|
||||
.WithAuthor(this.GetAuthor?.Invoke(entity))
|
||||
.ToXElement();
|
||||
|
||||
// Go through all the items inside the feed and add them.
|
||||
foreach (var entry in this.GetEntries!(entity))
|
||||
{
|
||||
feed.Add(entry.ToXElement());
|
||||
}
|
||||
|
||||
// Create the feed entity and return both objects.
|
||||
Entity feedEntity = new Entity()
|
||||
.Set(IsFeed.Instance)
|
||||
.Set(this.GetPath!(entity))
|
||||
.SetTextContent(feed + "\n");
|
||||
|
||||
return new[] { entity, feedEntity };
|
||||
}
|
||||
}
|
||||
}
|
15
src/Nitride.Feeds/HasFeed.cs
Normal file
15
src/Nitride.Feeds/HasFeed.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
namespace Nitride.Feeds
|
||||
{
|
||||
/// <summary>
|
||||
/// A marker component that indicates this entity has a feed associated with
|
||||
/// it.
|
||||
/// </summary>
|
||||
public class HasFeed
|
||||
{
|
||||
public HasFeed()
|
||||
{
|
||||
}
|
||||
|
||||
public static HasFeed Instance { get; } = new();
|
||||
}
|
||||
}
|
14
src/Nitride.Feeds/IsFeed.cs
Normal file
14
src/Nitride.Feeds/IsFeed.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace Nitride.Feeds
|
||||
{
|
||||
/// <summary>
|
||||
/// A marker component that indicates this page is a feed.
|
||||
/// </summary>
|
||||
public class IsFeed
|
||||
{
|
||||
public IsFeed()
|
||||
{
|
||||
}
|
||||
|
||||
public static IsFeed Instance { get; } = new();
|
||||
}
|
||||
}
|
36
src/Nitride.Feeds/Nitride.Feeds.csproj
Normal file
36
src/Nitride.Feeds/Nitride.Feeds.csproj
Normal file
|
@ -0,0 +1,36 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>An extension to Nitride static site generator to generate Atom feeds.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride.IO\Nitride.IO.csproj" />
|
||||
<ProjectReference Include="..\Nitride.Temporal\Nitride.Temporal.csproj" />
|
||||
<ProjectReference Include="..\Nitride\Nitride.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Gallium" Version="1.0.2" />
|
||||
<PackageReference Include="NodaTime" Version="3.0.5" />
|
||||
<PackageReference Include="Zio" Version="0.12.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Include the source generator -->
|
||||
<PropertyGroup>
|
||||
<EmitCompilerGeneratedFiles>True</EmitCompilerGeneratedFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride.Generators\Nitride.Generators.csproj">
|
||||
<OutputItemType>Analyzer</OutputItemType>
|
||||
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
16
src/Nitride.Feeds/NitrideFeedsBuilderExtensions.cs
Normal file
16
src/Nitride.Feeds/NitrideFeedsBuilderExtensions.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using Autofac;
|
||||
using Nitride.Temporal;
|
||||
|
||||
namespace Nitride.Feeds
|
||||
{
|
||||
public static class NitrideFeedsBuilderExtensions
|
||||
{
|
||||
public static NitrideBuilder UseFeeds(this NitrideBuilder builder)
|
||||
{
|
||||
return builder
|
||||
.UseTemporal()
|
||||
.ConfigureContainer(
|
||||
x => x.RegisterModule<NitrideFeedsModule>());
|
||||
}
|
||||
}
|
||||
}
|
6
src/Nitride.Feeds/NitrideFeedsModule.cs
Normal file
6
src/Nitride.Feeds/NitrideFeedsModule.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Nitride.Feeds
|
||||
{
|
||||
public class NitrideFeedsModule : NitrideModuleBase
|
||||
{
|
||||
}
|
||||
}
|
41
src/Nitride.Feeds/Structure/AtomAuthor.cs
Normal file
41
src/Nitride.Feeds/Structure/AtomAuthor.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using System.Xml.Linq;
|
||||
|
||||
namespace Nitride.Feeds.Structure
|
||||
{
|
||||
/// <summary>
|
||||
/// The type-safe structure for an author element.
|
||||
/// </summary>
|
||||
[WithProperties]
|
||||
public partial class AtomAuthor
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the author.
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an XML element out of the feed along with all items inside
|
||||
/// the feed.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public XElement? ToXElement()
|
||||
{
|
||||
if (this.Name == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var author = new XElement(XmlConstants.AtomNamespace + "author");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.Name))
|
||||
{
|
||||
author.Add(
|
||||
new XElement(
|
||||
XmlConstants.AtomNamespace + "name",
|
||||
new XText(this.Name)));
|
||||
}
|
||||
|
||||
return author;
|
||||
}
|
||||
}
|
||||
}
|
57
src/Nitride.Feeds/Structure/AtomCategory.cs
Normal file
57
src/Nitride.Feeds/Structure/AtomCategory.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Nitride.Feeds.Structure
|
||||
{
|
||||
/// <summary>
|
||||
/// The type-safe structure for a entry's category element.
|
||||
/// </summary>
|
||||
[WithProperties]
|
||||
public partial class AtomCategory
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the label associated with the category.
|
||||
/// </summary>
|
||||
public string? Label { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the scheme associated with the category.
|
||||
/// </summary>
|
||||
public Uri? Scheme { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the term of the category.
|
||||
/// </summary>
|
||||
public string? Term { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an XML element out of the feed along with all items inside
|
||||
/// the feed.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public XElement ToXElement()
|
||||
{
|
||||
if (this.Term == null)
|
||||
{
|
||||
throw new NullReferenceException(
|
||||
"Category term cannot be null.");
|
||||
}
|
||||
|
||||
var elem = new XElement(
|
||||
XmlConstants.AtomNamespace + "category",
|
||||
new XAttribute("term", this.Term));
|
||||
|
||||
if (this.Scheme != null)
|
||||
{
|
||||
elem.Add(new XAttribute("scheme", this.Scheme.ToString()));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(this.Label))
|
||||
{
|
||||
elem.Add(new XAttribute("label", this.Label));
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
}
|
121
src/Nitride.Feeds/Structure/AtomEntry.cs
Normal file
121
src/Nitride.Feeds/Structure/AtomEntry.cs
Normal file
|
@ -0,0 +1,121 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using NodaTime;
|
||||
using static Nitride.Feeds.Structure.XmlConstants;
|
||||
|
||||
namespace Nitride.Feeds.Structure
|
||||
{
|
||||
/// <summary>
|
||||
/// The type-safe structure for an entry in the Atom feed.
|
||||
/// </summary>
|
||||
[WithProperties]
|
||||
public partial class AtomEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the author for the feed.
|
||||
/// </summary>
|
||||
public AtomAuthor? Author { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the categories associated with this entry.
|
||||
/// </summary>
|
||||
public IEnumerable<AtomCategory>? Categories { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content of the entry.
|
||||
/// </summary>
|
||||
public string? Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of content (text, html) of the content.
|
||||
/// </summary>
|
||||
public string ContentType { get; set; } = "html";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ID of the feed.
|
||||
/// </summary>
|
||||
public string? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the summary of the entry.
|
||||
/// </summary>
|
||||
public string? Summary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of content (text, html) of the summary.
|
||||
/// </summary>
|
||||
public string SummaryType { get; set; } = "html";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title of the Feed.
|
||||
/// </summary>
|
||||
public string? Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp that the feed was updated.
|
||||
/// </summary>
|
||||
public Instant? Updated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the URL associated with this feed.
|
||||
/// </summary>
|
||||
public Uri? Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an XML element out of the feed along with all items inside
|
||||
/// the feed.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public XElement? ToXElement()
|
||||
{
|
||||
var elem = new XElement(AtomNamespace + "entry");
|
||||
|
||||
AtomHelper.AddIfSet(elem, "title", this.Title);
|
||||
|
||||
if (this.Url != null)
|
||||
{
|
||||
elem.Add(
|
||||
new XElement(
|
||||
AtomNamespace + "link",
|
||||
new XAttribute("rel", "alternate"),
|
||||
new XAttribute("href", this.Url.ToString())));
|
||||
}
|
||||
|
||||
AtomHelper.AddIfSet(
|
||||
elem,
|
||||
"updated",
|
||||
this.Updated?.ToString("g", null));
|
||||
AtomHelper.AddIfSet(elem, "id", this.Id);
|
||||
AtomHelper.AddIfSet(elem, this.Author?.ToXElement());
|
||||
|
||||
if (this.Categories != null)
|
||||
{
|
||||
foreach (var category in this.Categories)
|
||||
{
|
||||
elem.Add(category.ToXElement());
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(this.Summary))
|
||||
{
|
||||
elem.Add(
|
||||
new XElement(
|
||||
AtomNamespace + "summary",
|
||||
new XAttribute("type", this.SummaryType),
|
||||
new XText(this.Summary)));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(this.Content))
|
||||
{
|
||||
elem.Add(
|
||||
new XElement(
|
||||
AtomNamespace + "content",
|
||||
new XAttribute("type", this.ContentType),
|
||||
new XText(this.Content)));
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
}
|
104
src/Nitride.Feeds/Structure/AtomFeed.cs
Normal file
104
src/Nitride.Feeds/Structure/AtomFeed.cs
Normal file
|
@ -0,0 +1,104 @@
|
|||
using System;
|
||||
using System.Xml.Linq;
|
||||
using NodaTime;
|
||||
using static Nitride.Feeds.Structure.XmlConstants;
|
||||
|
||||
namespace Nitride.Feeds.Structure
|
||||
{
|
||||
/// <summary>
|
||||
/// The type-safe structure of the top-level feed.
|
||||
/// </summary>
|
||||
[WithProperties]
|
||||
public partial class AtomFeed
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the MIME type for the alternate URL.
|
||||
/// </summary>
|
||||
public string AlternateMimeType { get; set; } = "text/html";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alternate URL for this feed.
|
||||
/// </summary>
|
||||
public Uri? AlternateUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the author for the feed.
|
||||
/// </summary>
|
||||
public AtomAuthor? Author { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ID of the feed.
|
||||
/// </summary>
|
||||
public string? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rights (license) of the feed.
|
||||
/// </summary>
|
||||
public string? Rights { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title of the Feed.
|
||||
/// </summary>
|
||||
public string? Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp that the feed was updated.
|
||||
/// </summary>
|
||||
public Instant? Updated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the URL associated with this feed.
|
||||
/// </summary>
|
||||
public Uri? Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an XML element out of the feed along with all items inside
|
||||
/// the feed.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public XElement ToXElement()
|
||||
{
|
||||
var elem = new XElement(AtomNamespace + "feed");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(this.Title))
|
||||
{
|
||||
elem.Add(
|
||||
new XElement(
|
||||
AtomNamespace + "title",
|
||||
new XAttribute("type", "text"),
|
||||
new XAttribute(XNamespace.Xml + "lang", "en"),
|
||||
new XText(this.Title)));
|
||||
}
|
||||
|
||||
if (this.Url != null)
|
||||
{
|
||||
elem.Add(
|
||||
new XElement(
|
||||
AtomNamespace + "link",
|
||||
new XAttribute("type", "application/atom+xml"),
|
||||
new XAttribute("href", this.Url.ToString()),
|
||||
new XAttribute("rel", "self")));
|
||||
}
|
||||
|
||||
if (this.AlternateUrl != null)
|
||||
{
|
||||
elem.Add(
|
||||
new XElement(
|
||||
AtomNamespace + "link",
|
||||
new XAttribute("type", this.AlternateMimeType),
|
||||
new XAttribute("href", this.AlternateUrl.ToString()),
|
||||
new XAttribute("rel", "alternate")));
|
||||
}
|
||||
|
||||
AtomHelper.AddIfSet(
|
||||
elem,
|
||||
"updated",
|
||||
this.Updated?.ToString("g", null));
|
||||
AtomHelper.AddIfSet(elem, "id", this.Id);
|
||||
AtomHelper.AddIfSet(elem, this.Author?.ToXElement());
|
||||
AtomHelper.AddIfSet(elem, "rights", this.Rights);
|
||||
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
}
|
29
src/Nitride.Feeds/Structure/AtomHelper.cs
Normal file
29
src/Nitride.Feeds/Structure/AtomHelper.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using System.Xml.Linq;
|
||||
|
||||
namespace Nitride.Feeds.Structure
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper methods for working with XML elements.
|
||||
/// </summary>
|
||||
public static class AtomHelper
|
||||
{
|
||||
public static void AddIfSet(XElement root, XElement? elem)
|
||||
{
|
||||
if (elem != null)
|
||||
{
|
||||
root.Add(elem);
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddIfSet(XElement elem, string name, string? text)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
elem.Add(
|
||||
new XElement(
|
||||
XmlConstants.AtomNamespace + name,
|
||||
new XText(text)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
22
src/Nitride.Feeds/Structure/XmlConstants.cs
Normal file
22
src/Nitride.Feeds/Structure/XmlConstants.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using System.Xml.Linq;
|
||||
|
||||
namespace Nitride.Feeds.Structure
|
||||
{
|
||||
/// <summary>
|
||||
/// Common constants used while generating feeds.
|
||||
/// </summary>
|
||||
public static class XmlConstants
|
||||
{
|
||||
/// <summary>
|
||||
/// The XML namespace for Atom feeds.
|
||||
/// </summary>
|
||||
public static readonly XNamespace AtomNamespace =
|
||||
"http://www.w3.org/2005/Atom";
|
||||
|
||||
/// <summary>
|
||||
/// The XML namespace for media.
|
||||
/// </summary>
|
||||
public static readonly XNamespace MediaNamespace =
|
||||
"http://search.yahoo.com/mrss/";
|
||||
}
|
||||
}
|
11
src/Nitride.Gemtext/IsGemtext.cs
Normal file
11
src/Nitride.Gemtext/IsGemtext.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Nitride.Gemtext
|
||||
{
|
||||
/// <summary>
|
||||
/// A marker component for indicating that an entity is Gemtext, the format
|
||||
/// for text files using the Gemini protocol.
|
||||
/// </summary>
|
||||
public class IsGemtext
|
||||
{
|
||||
public static IsGemtext Instance { get; } = new IsGemtext();
|
||||
}
|
||||
}
|
27
src/Nitride.Gemtext/Nitride.Gemtext.csproj
Normal file
27
src/Nitride.Gemtext/Nitride.Gemtext.csproj
Normal file
|
@ -0,0 +1,27 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>An extension to Nitride static site generator to generate Gemtext output.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride\Nitride.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Include the source generator -->
|
||||
<PropertyGroup>
|
||||
<EmitCompilerGeneratedFiles>True</EmitCompilerGeneratedFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride.Generators\Nitride.Generators.csproj">
|
||||
<OutputItemType>Analyzer</OutputItemType>
|
||||
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
14
src/Nitride.Gemtext/NitrideGemtextBuilderExtensions.cs
Normal file
14
src/Nitride.Gemtext/NitrideGemtextBuilderExtensions.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Autofac;
|
||||
|
||||
namespace Nitride.Gemtext
|
||||
{
|
||||
public static class NitrideGemtextBuilderExtensions
|
||||
{
|
||||
public static NitrideBuilder UseGemtext(this NitrideBuilder builder)
|
||||
{
|
||||
return builder
|
||||
.ConfigureContainer(
|
||||
x => x.RegisterModule<NitrideGemtextModule>());
|
||||
}
|
||||
}
|
||||
}
|
6
src/Nitride.Gemtext/NitrideGemtextModule.cs
Normal file
6
src/Nitride.Gemtext/NitrideGemtextModule.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Nitride.Gemtext
|
||||
{
|
||||
public class NitrideGemtextModule : NitrideModuleBase
|
||||
{
|
||||
}
|
||||
}
|
159
src/Nitride.Generators/CodeAnalysisExtensions.cs
Normal file
159
src/Nitride.Generators/CodeAnalysisExtensions.cs
Normal file
|
@ -0,0 +1,159 @@
|
|||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Nitride.Generators
|
||||
{
|
||||
/// <summary>
|
||||
/// Various wrappers around the diagnostics to simplify generation.
|
||||
/// </summary>
|
||||
public static class CodeAnalysisExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an error message to break the build while generating code.
|
||||
/// </summary>
|
||||
/// <param name="context">The context that contains the diagnostic.</param>
|
||||
/// <param name="messageCode">The normalized message code.</param>
|
||||
/// <param name="format">The string format for the message.</param>
|
||||
/// <param name="parameters">The optional parameters.</param>
|
||||
public static void Error(
|
||||
this GeneratorExecutionContext context,
|
||||
MessageCode messageCode,
|
||||
string format,
|
||||
params object[] parameters)
|
||||
{
|
||||
Error(context, messageCode, null, format, parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an error message to break the build while generating code.
|
||||
/// </summary>
|
||||
/// <param name="context">The context that contains the diagnostic.</param>
|
||||
/// <param name="messageCode">The normalized message code.</param>
|
||||
/// <param name="location">The optional location for the message.</param>
|
||||
/// <param name="format">The string format for the message.</param>
|
||||
/// <param name="parameters">The optional parameters.</param>
|
||||
public static void Error(
|
||||
this GeneratorExecutionContext context,
|
||||
MessageCode messageCode,
|
||||
Location? location,
|
||||
string format,
|
||||
params object[] parameters)
|
||||
{
|
||||
context.Message(
|
||||
messageCode,
|
||||
location,
|
||||
DiagnosticSeverity.Error,
|
||||
format,
|
||||
parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an informational message to break the build while generating code.
|
||||
/// </summary>
|
||||
/// <param name="context">The context that contains the diagnostic.</param>
|
||||
/// <param name="messageCode">The normalized message code.</param>
|
||||
/// <param name="format">The string format for the message.</param>
|
||||
/// <param name="parameters">The optional parameters.</param>
|
||||
public static void Information(
|
||||
this GeneratorExecutionContext context,
|
||||
MessageCode messageCode,
|
||||
string format,
|
||||
params object[] parameters)
|
||||
{
|
||||
Information(context, messageCode, null, format, parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an informational message to break the build while generating code.
|
||||
/// </summary>
|
||||
/// <param name="context">The context that contains the diagnostic.</param>
|
||||
/// <param name="messageCode">The normalized message code.</param>
|
||||
/// <param name="location">The optional location for the message.</param>
|
||||
/// <param name="format">The string format for the message.</param>
|
||||
/// <param name="parameters">The optional parameters.</param>
|
||||
public static void Information(
|
||||
this GeneratorExecutionContext context,
|
||||
MessageCode messageCode,
|
||||
Location? location,
|
||||
string format,
|
||||
params object[] parameters)
|
||||
{
|
||||
context.Message(
|
||||
messageCode,
|
||||
location,
|
||||
DiagnosticSeverity.Info,
|
||||
format,
|
||||
parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a warning message to break the build while generating code.
|
||||
/// </summary>
|
||||
/// <param name="context">The context that contains the diagnostic.</param>
|
||||
/// <param name="messageCode">The normalized message code.</param>
|
||||
/// <param name="format">The string format for the message.</param>
|
||||
/// <param name="parameters">The optional parameters.</param>
|
||||
public static void Warning(
|
||||
this GeneratorExecutionContext context,
|
||||
MessageCode messageCode,
|
||||
string format,
|
||||
params object[] parameters)
|
||||
{
|
||||
Warning(context, messageCode, null, format, parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a warning message to break the build while generating code.
|
||||
/// </summary>
|
||||
/// <param name="context">The context that contains the diagnostic.</param>
|
||||
/// <param name="messageCode">The normalized message code.</param>
|
||||
/// <param name="location">The optional location for the message.</param>
|
||||
/// <param name="format">The string format for the message.</param>
|
||||
/// <param name="parameters">The optional parameters.</param>
|
||||
public static void Warning(
|
||||
this GeneratorExecutionContext context,
|
||||
MessageCode messageCode,
|
||||
Location? location,
|
||||
string format,
|
||||
params object[] parameters)
|
||||
{
|
||||
context.Message(
|
||||
messageCode,
|
||||
location,
|
||||
DiagnosticSeverity.Warning,
|
||||
format,
|
||||
parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a message to break the build while generating code.
|
||||
/// </summary>
|
||||
/// <param name="context">The context that contains the diagnostic.</param>
|
||||
/// <param name="messageCode">The normalized message code.</param>
|
||||
/// <param name="location">The optional location for the message.</param>
|
||||
/// <param name="format">The string format for the message.</param>
|
||||
/// <param name="parameters">The optional parameters.</param>
|
||||
/// <param name="severity">The severity of the message.</param>
|
||||
private static void Message(
|
||||
this GeneratorExecutionContext context,
|
||||
MessageCode messageCode,
|
||||
Location? location,
|
||||
DiagnosticSeverity severity,
|
||||
string format,
|
||||
params object[] parameters)
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
Diagnostic.Create(
|
||||
"GN" + ((int)messageCode).ToString("D4"),
|
||||
"Nitride",
|
||||
string.Format(format, parameters),
|
||||
severity,
|
||||
severity,
|
||||
true,
|
||||
severity is DiagnosticSeverity.Warning
|
||||
or DiagnosticSeverity.Info
|
||||
? 4
|
||||
: 0,
|
||||
location: location));
|
||||
}
|
||||
}
|
||||
}
|
10
src/Nitride.Generators/MessageCode.cs
Normal file
10
src/Nitride.Generators/MessageCode.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Nitride.Generators
|
||||
{
|
||||
/// <summary>
|
||||
/// All the error messages produced by the generators.
|
||||
/// </summary>
|
||||
public enum MessageCode
|
||||
{
|
||||
Debug = 1,
|
||||
}
|
||||
}
|
19
src/Nitride.Generators/Nitride.Generators.csproj
Normal file
19
src/Nitride.Generators/Nitride.Generators.csproj
Normal file
|
@ -0,0 +1,19 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>Common source generators for Nitride.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Gallium" Version="1.0.2" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="3.8.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.8.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
209
src/Nitride.Generators/WithPropertySourceGenerator.cs
Normal file
209
src/Nitride.Generators/WithPropertySourceGenerator.cs
Normal file
|
@ -0,0 +1,209 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Nitride.Generators
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a source generator that creates Set* methods for the various
|
||||
/// properties that also returns the same object for purposes of chaining
|
||||
/// together calls.
|
||||
/// </summary>
|
||||
[Generator]
|
||||
public class WithPropertySourceGenerator : ISourceGenerator
|
||||
{
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
// Get the generator infrastructure will create a receiver and
|
||||
// populate it we can retrieve the populated instance via the
|
||||
// context.
|
||||
var syntaxReceiver = (SyntaxReceiver?)context.SyntaxReceiver;
|
||||
|
||||
if (syntaxReceiver == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Report any messages.
|
||||
foreach (var message in syntaxReceiver.Messages)
|
||||
{
|
||||
context.Information(
|
||||
MessageCode.Debug,
|
||||
Location.Create(
|
||||
"Temporary.g.cs",
|
||||
TextSpan.FromBounds(0, 0),
|
||||
new LinePositionSpan(
|
||||
new LinePosition(0, 0),
|
||||
new LinePosition(0, 0))),
|
||||
"Generating additional identifier code: {0}",
|
||||
message);
|
||||
}
|
||||
|
||||
// If we didn't find anything, then there is nothing to do.
|
||||
if (syntaxReceiver.ClassesToAugment.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Go through each one.
|
||||
foreach (var cds in syntaxReceiver.ClassesToAugment)
|
||||
{
|
||||
this.GenerateClassFile(context, cds);
|
||||
}
|
||||
}
|
||||
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
{
|
||||
// Register a factory that can create our custom syntax receiver
|
||||
context.RegisterForSyntaxNotifications(
|
||||
() => new SyntaxReceiver(context));
|
||||
}
|
||||
|
||||
private void GenerateClassFile(
|
||||
GeneratorExecutionContext context,
|
||||
ClassDeclarationSyntax cds)
|
||||
{
|
||||
// Get the namespace.
|
||||
var nds = (NamespaceDeclarationSyntax?)cds.Parent;
|
||||
|
||||
if (nds == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string? ns = nds.Name.ToString();
|
||||
|
||||
// Create the partial class.
|
||||
StringBuilder buffer = new();
|
||||
buffer.AppendLine("#nullable enable");
|
||||
|
||||
// Copy the using statements from the file.
|
||||
if (nds.Parent is CompilationUnitSyntax cus)
|
||||
{
|
||||
foreach (var uds in cus.Usings)
|
||||
{
|
||||
buffer.AppendLine(uds.ToString());
|
||||
}
|
||||
|
||||
buffer.AppendLine();
|
||||
}
|
||||
|
||||
// Create the namespace.
|
||||
buffer.AppendLine($"namespace {ns}");
|
||||
buffer.AppendLine("{");
|
||||
buffer.AppendLine($" public partial class {cds.Identifier}");
|
||||
buffer.AppendLine(" {");
|
||||
|
||||
// Go through the properties of the namespace.
|
||||
IEnumerable<PropertyDeclarationSyntax> properties = cds.Members
|
||||
.Where(m => m.Kind() == SyntaxKind.PropertyDeclaration)
|
||||
.Cast<PropertyDeclarationSyntax>();
|
||||
bool first = true;
|
||||
|
||||
foreach (PropertyDeclarationSyntax pds in properties)
|
||||
{
|
||||
// See if we have a setter.
|
||||
bool found =
|
||||
pds.AccessorList?.Accessors
|
||||
.Any(x => x.Keyword.ToString() == "set")
|
||||
?? false;
|
||||
|
||||
if (!found)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we aren't first, then add a newline before it.
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.AppendLine();
|
||||
}
|
||||
|
||||
// Write some documentation.
|
||||
buffer.AppendLine(" /// <summary>");
|
||||
buffer.AppendLine(
|
||||
string.Format(
|
||||
" /// Sets the {0} value and returns the operation for chaining.",
|
||||
pds.Identifier));
|
||||
buffer.AppendLine(" /// </summary>");
|
||||
|
||||
// We have the components for writing out a setter.
|
||||
buffer.AppendLine(
|
||||
string.Format(
|
||||
" public {0} With{1}({2} value)",
|
||||
cds.Identifier,
|
||||
pds.Identifier,
|
||||
pds.Type));
|
||||
buffer.AppendLine(" {");
|
||||
buffer.AppendLine(
|
||||
string.Format(
|
||||
" this.{0} = value;",
|
||||
pds.Identifier));
|
||||
buffer.AppendLine(" return this;");
|
||||
buffer.AppendLine(" }");
|
||||
}
|
||||
|
||||
// Finish up the class.
|
||||
buffer.AppendLine(" }");
|
||||
buffer.AppendLine("}");
|
||||
|
||||
// Create the source text and write out the file.
|
||||
SourceText sourceText = SourceText.From(
|
||||
buffer.ToString(),
|
||||
Encoding.UTF8);
|
||||
context.AddSource(cds.Identifier + ".Generated.cs", sourceText);
|
||||
}
|
||||
|
||||
private class SyntaxReceiver : ISyntaxReceiver
|
||||
{
|
||||
private readonly GeneratorInitializationContext context;
|
||||
|
||||
public SyntaxReceiver(GeneratorInitializationContext context)
|
||||
{
|
||||
this.context = context;
|
||||
this.ClassesToAugment = new List<ClassDeclarationSyntax>();
|
||||
this.Messages = new List<string>();
|
||||
}
|
||||
|
||||
public List<ClassDeclarationSyntax> ClassesToAugment { get; }
|
||||
|
||||
public List<string> Messages { get; }
|
||||
|
||||
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
|
||||
{
|
||||
// We only care about class declarations.
|
||||
if (syntaxNode is not ClassDeclarationSyntax cds)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// See if the class has our set properties attribute.
|
||||
bool found = cds
|
||||
.AttributeLists
|
||||
.AsEnumerable()
|
||||
.SelectMany(x => x.Attributes)
|
||||
.Select(x => x.Name.ToString())
|
||||
.Any(
|
||||
x => x switch
|
||||
{
|
||||
"WithProperties" => true,
|
||||
"WithPropertiesAttribute" => true,
|
||||
_ => false,
|
||||
});
|
||||
|
||||
if (found)
|
||||
{
|
||||
this.ClassesToAugment.Add(cds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
74
src/Nitride.Handlebars/ApplyContentHandlebarsTemplate.cs
Normal file
74
src/Nitride.Handlebars/ApplyContentHandlebarsTemplate.cs
Normal file
|
@ -0,0 +1,74 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Gallium;
|
||||
using HandlebarsDotNet;
|
||||
using Nitride.Contents;
|
||||
|
||||
namespace Nitride.Handlebars
|
||||
{
|
||||
/// <summary>
|
||||
/// An operation that uses the content to create a template that is then
|
||||
/// applied against the content and metadata and then replaces the content
|
||||
/// of that entity.
|
||||
/// </summary>
|
||||
public class ApplyContentHandlebarsTemplate<TModel> : NitrideOperationBase
|
||||
{
|
||||
private readonly HandlebarsTemplateCache cache;
|
||||
|
||||
public ApplyContentHandlebarsTemplate(HandlebarsTemplateCache cache)
|
||||
{
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the callback used to create a model from a given
|
||||
/// entity. This allows for the website to customize what information is
|
||||
/// being passed to the template.
|
||||
/// </summary>
|
||||
public Func<Entity, TModel>? CreateModelCallback { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
this.CheckNotNull(x => x.CreateModelCallback);
|
||||
|
||||
return input
|
||||
.ForEachEntity<HasHandlebarsTemplate, ITextContent>(this.Apply);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the callback for the template and returns the operation to
|
||||
/// chain operations.
|
||||
/// </summary>
|
||||
/// <param name="callback">The callback to set.</param>
|
||||
/// <returns>The ApplyContentHandlebarsTemplate to chain requests.</returns>
|
||||
public ApplyContentHandlebarsTemplate<TModel> WithCreateModelCallback(
|
||||
Func<Entity, TModel>? callback)
|
||||
{
|
||||
this.CreateModelCallback = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
private Entity Apply(
|
||||
Entity entity,
|
||||
HasHandlebarsTemplate _,
|
||||
ITextContent content)
|
||||
{
|
||||
// Create the model using the callback.
|
||||
TModel model = this.CreateModelCallback!(entity);
|
||||
|
||||
// Create a template from the contents.
|
||||
string text = content.GetText();
|
||||
HandlebarsTemplate<object, object> template =
|
||||
this.cache.GetLiteralTemplate(text);
|
||||
|
||||
// Render the template and create a new entity with the updated
|
||||
// text.
|
||||
string result = template(model!);
|
||||
|
||||
return entity
|
||||
.Remove<HasHandlebarsTemplate>()
|
||||
.SetTextContent(new StringTextContent(result));
|
||||
}
|
||||
}
|
||||
}
|
88
src/Nitride.Handlebars/ApplyStyleHandlebarsTemplate.cs
Normal file
88
src/Nitride.Handlebars/ApplyStyleHandlebarsTemplate.cs
Normal file
|
@ -0,0 +1,88 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Gallium;
|
||||
using HandlebarsDotNet;
|
||||
using Nitride.Contents;
|
||||
|
||||
namespace Nitride.Handlebars
|
||||
{
|
||||
/// <summary>
|
||||
/// An operation that applies a common or shared template on the content of
|
||||
/// a document that includes theme or styling information.
|
||||
/// </summary>
|
||||
public class ApplyStyleHandlebarsTemplate<TModel> : NitrideOperationBase
|
||||
{
|
||||
private readonly HandlebarsTemplateCache cache;
|
||||
|
||||
public ApplyStyleHandlebarsTemplate(HandlebarsTemplateCache cache)
|
||||
{
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the callback used to create a model from a given
|
||||
/// entity. This allows for the website to customize what information is
|
||||
/// being passed to the template.
|
||||
/// </summary>
|
||||
public Func<Entity, TModel>? CreateModelCallback { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the callback used to determine which template to use
|
||||
/// for a given entity. This lets one have a per-page template change.
|
||||
/// </summary>
|
||||
public Func<Entity, string>? GetTemplateName { get; set; }
|
||||
|
||||
public IHandlebars? Handlebars { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
// Make sure we have sane data.
|
||||
this.CheckNotNull(x => x.CreateModelCallback);
|
||||
this.CheckNotNull(x => x.Handlebars);
|
||||
this.CheckNotNull(x => x.GetTemplateName);
|
||||
|
||||
// Create and set up the Handlebars.
|
||||
return input
|
||||
.ForEachEntity<ITextContent>(this.Apply);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the callback for the template and returns the operation to
|
||||
/// chain operations.
|
||||
/// </summary>
|
||||
/// <param name="callback">The callback to set.</param>
|
||||
/// <returns>The ApplyContentHandlebarsTemplate to chain requests.</returns>
|
||||
public ApplyStyleHandlebarsTemplate<TModel> WithCreateModelCallback(
|
||||
Func<Entity, TModel>? callback)
|
||||
{
|
||||
this.CreateModelCallback = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApplyStyleHandlebarsTemplate<TModel> WithGetTemplateName(
|
||||
Func<Entity, string>? callback)
|
||||
{
|
||||
this.GetTemplateName = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApplyStyleHandlebarsTemplate<TModel> WithHandlebars(
|
||||
IHandlebars? handlebars)
|
||||
{
|
||||
this.Handlebars = handlebars;
|
||||
return this;
|
||||
}
|
||||
|
||||
private Entity Apply(Entity entity, ITextContent content)
|
||||
{
|
||||
TModel model = this.CreateModelCallback!(entity);
|
||||
string name = this.GetTemplateName!(entity);
|
||||
HandlebarsTemplate<object, object> template =
|
||||
this.cache.GetNamedTemplate(name);
|
||||
string result = template(model!);
|
||||
|
||||
return entity.SetTextContent(result);
|
||||
}
|
||||
}
|
||||
}
|
70
src/Nitride.Handlebars/HandlebarsTemplateCache.cs
Normal file
70
src/Nitride.Handlebars/HandlebarsTemplateCache.cs
Normal file
|
@ -0,0 +1,70 @@
|
|||
using System.Collections.Generic;
|
||||
using HandlebarsDotNet;
|
||||
using Open.Threading;
|
||||
|
||||
namespace Nitride.Handlebars
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a cache for templates to prevent compiling the same template
|
||||
/// more than once.
|
||||
/// </summary>
|
||||
public class HandlebarsTemplateCache
|
||||
{
|
||||
private readonly IHandlebars handlebars;
|
||||
|
||||
private readonly ModificationSynchronizer locker;
|
||||
|
||||
private readonly Dictionary<string, HandlebarsTemplate<object, object>>
|
||||
templates;
|
||||
|
||||
public HandlebarsTemplateCache(IHandlebars handlebars)
|
||||
{
|
||||
this.handlebars = handlebars;
|
||||
this.locker = new ModificationSynchronizer();
|
||||
this.templates =
|
||||
new Dictionary<string, HandlebarsTemplate<object, object>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Caches the template by name based on the contents of the disk. It
|
||||
/// does it once in a multi-threaded manner to ensure it is only cached
|
||||
/// once in memory.
|
||||
/// </summary>
|
||||
/// <param name="literal">The string that contains the template.</param>
|
||||
/// <returns></returns>
|
||||
public HandlebarsTemplate<object, object> GetLiteralTemplate(
|
||||
string literal)
|
||||
{
|
||||
// Start with a read lock to see if we've already compiled it.
|
||||
this.locker.Modifying(
|
||||
() => !this.templates.ContainsKey(literal),
|
||||
() =>
|
||||
{
|
||||
HandlebarsTemplate<object, object> template =
|
||||
this.handlebars
|
||||
!.Compile(literal);
|
||||
|
||||
this.templates[literal] = template;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return this.locker.Reading(() => this.templates[literal]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Caches the template by name based on the contents of the disk. It
|
||||
/// does it once in a multi-threaded manner to ensure it is only cached
|
||||
/// once in memory.
|
||||
/// </summary>
|
||||
/// <param name="templateName"></param>
|
||||
/// <returns></returns>
|
||||
public HandlebarsTemplate<object, object> GetNamedTemplate(
|
||||
string templateName)
|
||||
{
|
||||
string template = $"{{{{> {templateName}}}}}";
|
||||
|
||||
return this.GetLiteralTemplate(template);
|
||||
}
|
||||
}
|
||||
}
|
11
src/Nitride.Handlebars/HasHandlebarsTemplate.cs
Normal file
11
src/Nitride.Handlebars/HasHandlebarsTemplate.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Nitride.Handlebars
|
||||
{
|
||||
/// <summary>
|
||||
/// A marker component that indicates that a given file with text component
|
||||
/// has a Handlebars template in it.
|
||||
/// </summary>
|
||||
public class HasHandlebarsTemplate
|
||||
{
|
||||
public static HasHandlebarsTemplate Instance { get; } = new();
|
||||
}
|
||||
}
|
32
src/Nitride.Handlebars/IdentifyHandlebars.cs
Normal file
32
src/Nitride.Handlebars/IdentifyHandlebars.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using System.Collections.Generic;
|
||||
using Gallium;
|
||||
using Nitride.Contents;
|
||||
|
||||
namespace Nitride.Handlebars
|
||||
{
|
||||
/// <summary>
|
||||
/// An operation that discovers which text files have a Handlebars template
|
||||
/// inside them.
|
||||
/// </summary>
|
||||
public class IdentifyHandlebars : INitrideOperation
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
return input
|
||||
.ForEachEntity<ITextContent>(this.ScanContent);
|
||||
}
|
||||
|
||||
private Entity ScanContent(Entity entity, ITextContent content)
|
||||
{
|
||||
string text = content.GetText();
|
||||
|
||||
if (text.Contains("{{") && text.Contains("}}"))
|
||||
{
|
||||
return entity.Set(HasHandlebarsTemplate.Instance);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
}
|
36
src/Nitride.Handlebars/Nitride.Handlebars.csproj
Normal file
36
src/Nitride.Handlebars/Nitride.Handlebars.csproj
Normal file
|
@ -0,0 +1,36 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>An extension to Nitride static site generator to style output with Handlebars.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac" Version="6.2.0" />
|
||||
<PackageReference Include="Gallium" Version="1.0.2" />
|
||||
<PackageReference Include="Handlebars.Net" Version="2.0.8" />
|
||||
<PackageReference Include="NodaTime.Testing" Version="3.0.5" />
|
||||
<PackageReference Include="Open.Threading" Version="1.6.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride\Nitride.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Include the source generator -->
|
||||
<PropertyGroup>
|
||||
<EmitCompilerGeneratedFiles>True</EmitCompilerGeneratedFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride.Generators\Nitride.Generators.csproj">
|
||||
<OutputItemType>Analyzer</OutputItemType>
|
||||
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
14
src/Nitride.Handlebars/NitrideHandlebarsBuilderExtensions.cs
Normal file
14
src/Nitride.Handlebars/NitrideHandlebarsBuilderExtensions.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Autofac;
|
||||
|
||||
namespace Nitride.Handlebars
|
||||
{
|
||||
public static class NitrideHandlebarsBuilderExtensions
|
||||
{
|
||||
public static NitrideBuilder UseHandlebars(this NitrideBuilder builder)
|
||||
{
|
||||
return builder
|
||||
.ConfigureContainer(
|
||||
x => x.RegisterModule<NitrideHandlebarsModule>());
|
||||
}
|
||||
}
|
||||
}
|
22
src/Nitride.Handlebars/NitrideHandlebarsModule.cs
Normal file
22
src/Nitride.Handlebars/NitrideHandlebarsModule.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using Autofac;
|
||||
|
||||
namespace Nitride.Handlebars
|
||||
{
|
||||
public class NitrideHandlebarsModule : NitrideModuleBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Register(ContainerBuilder builder)
|
||||
{
|
||||
builder
|
||||
.RegisterType<HandlebarsTemplateCache>()
|
||||
.AsSelf()
|
||||
.SingleInstance();
|
||||
builder
|
||||
.RegisterGeneric(typeof(ApplyContentHandlebarsTemplate<>))
|
||||
.As(typeof(ApplyContentHandlebarsTemplate<>));
|
||||
builder
|
||||
.RegisterGeneric(typeof(ApplyStyleHandlebarsTemplate<>))
|
||||
.As(typeof(ApplyStyleHandlebarsTemplate<>));
|
||||
}
|
||||
}
|
||||
}
|
26
src/Nitride.Handlebars/README.md
Normal file
26
src/Nitride.Handlebars/README.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
Nitride.Handlebars
|
||||
==================
|
||||
|
||||
This is a collection
|
||||
of [Handlebars](https://github.com/Handlebars-Net/Handlebars.Net)
|
||||
operations that work with Nitride. There are two ways of using Handelbars in
|
||||
this package.
|
||||
|
||||
## IHandlebars
|
||||
|
||||
This library does *not* configure or register `IHandlebars` but requires it to
|
||||
be injected into the DI container (Autofac) along with any templates or inlines
|
||||
loaded.
|
||||
|
||||
## Styling Templates
|
||||
|
||||
The first is to use it to apply a Handlebars theme to a page or text content.
|
||||
This would include adding links to the CSS, any javascript, generating menus,
|
||||
and common navigation elements.
|
||||
|
||||
## Content Templates
|
||||
|
||||
The second is used to apply it to the Handlebars inside the text content. This
|
||||
is the page-specific content that needs to be resolved. A good example of this
|
||||
might be creating a "last five posts" page or embedding some metadata from the
|
||||
YAML header into the page.
|
31
src/Nitride.Html/HtmlEntitiesToUnicode.cs
Normal file
31
src/Nitride.Html/HtmlEntitiesToUnicode.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Gallium;
|
||||
using Nitride.Contents;
|
||||
|
||||
namespace Nitride.Html
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the text input that uses HTML entities and turns them into
|
||||
/// Unicode variations.
|
||||
/// </summary>
|
||||
public class HtmlEntitiesToUnicode : NitrideOperationBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
return input
|
||||
.ForEachEntity<ITextContent>(this.ResolveHtmlEntities);
|
||||
}
|
||||
|
||||
private Entity ResolveHtmlEntities(
|
||||
Entity entity,
|
||||
ITextContent content)
|
||||
{
|
||||
string text = content.GetText();
|
||||
string resolved = WebUtility.HtmlDecode(text);
|
||||
|
||||
return entity.SetTextContent(resolved);
|
||||
}
|
||||
}
|
||||
}
|
10
src/Nitride.Html/IsHtml.cs
Normal file
10
src/Nitride.Html/IsHtml.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Nitride.Html
|
||||
{
|
||||
/// <summary>
|
||||
/// A marker component that indicates that the entity is an HTML file.
|
||||
/// </summary>
|
||||
public class IsHtml
|
||||
{
|
||||
public static IsHtml Instance { get; } = new();
|
||||
}
|
||||
}
|
31
src/Nitride.Html/Nitride.Html.csproj
Normal file
31
src/Nitride.Html/Nitride.Html.csproj
Normal file
|
@ -0,0 +1,31 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>An extension to Nitride static site generator to generate HTML output.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride\Nitride.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Include the source generator -->
|
||||
<PropertyGroup>
|
||||
<EmitCompilerGeneratedFiles>True</EmitCompilerGeneratedFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride.Generators\Nitride.Generators.csproj">
|
||||
<OutputItemType>Analyzer</OutputItemType>
|
||||
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Gallium" Version="1.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
14
src/Nitride.Html/NitrideHtmlBuilderExtensions.cs
Normal file
14
src/Nitride.Html/NitrideHtmlBuilderExtensions.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Autofac;
|
||||
|
||||
namespace Nitride.Html
|
||||
{
|
||||
public static class NitrideHtmlBuilderExtensions
|
||||
{
|
||||
public static NitrideBuilder UseHtml(this NitrideBuilder builder)
|
||||
{
|
||||
return builder
|
||||
.ConfigureContainer(
|
||||
x => x.RegisterModule<NitrideHtmlModule>());
|
||||
}
|
||||
}
|
||||
}
|
6
src/Nitride.Html/NitrideHtmlModule.cs
Normal file
6
src/Nitride.Html/NitrideHtmlModule.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Nitride.Html
|
||||
{
|
||||
public class NitrideHtmlModule : NitrideModuleBase
|
||||
{
|
||||
}
|
||||
}
|
48
src/Nitride.IO.Tests/AddPathPrefixTests.cs
Normal file
48
src/Nitride.IO.Tests/AddPathPrefixTests.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using System.Linq;
|
||||
using Autofac;
|
||||
using Nitride.IO.Contents;
|
||||
using Nitride.IO.Paths;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Zio;
|
||||
using Zio.FileSystems;
|
||||
|
||||
namespace Nitride.IO.Tests
|
||||
{
|
||||
public class AddPathPrefixTests : NitrideIOTestsBase
|
||||
{
|
||||
private readonly MemoryFileSystem fileSystem;
|
||||
|
||||
public AddPathPrefixTests(ITestOutputHelper output)
|
||||
: base(output)
|
||||
{
|
||||
this.fileSystem = new MemoryFileSystem();
|
||||
this.fileSystem.CreateFile("/b1.txt");
|
||||
this.fileSystem.CreateFile("/c1.md");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PrefixAllFiles()
|
||||
{
|
||||
// Set up the operation.
|
||||
var readFiles = this.Container.Resolve<ReadFiles.Factory>();
|
||||
var op = new AddPathPrefix("/prefix");
|
||||
|
||||
// Read and replace the paths.
|
||||
IOrderedEnumerable<string> output = readFiles(this.fileSystem)
|
||||
.Read()
|
||||
.Run(op)
|
||||
.Select(x => x.Get<UPath>().ToString())
|
||||
.OrderBy(x => x);
|
||||
|
||||
// Verify the results.
|
||||
Assert.Equal(
|
||||
new[]
|
||||
{
|
||||
"/prefix/b1.txt",
|
||||
"/prefix/c1.md",
|
||||
},
|
||||
output);
|
||||
}
|
||||
}
|
||||
}
|
80
src/Nitride.IO.Tests/MoveToIndexPathsTests.cs
Normal file
80
src/Nitride.IO.Tests/MoveToIndexPathsTests.cs
Normal file
|
@ -0,0 +1,80 @@
|
|||
using System.Linq;
|
||||
using Autofac;
|
||||
using Nitride.IO.Contents;
|
||||
using Nitride.IO.Paths;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Zio;
|
||||
using Zio.FileSystems;
|
||||
|
||||
namespace Nitride.IO.Tests
|
||||
{
|
||||
public class MoveToIndexPathsTests : NitrideIOTestsBase
|
||||
{
|
||||
private readonly MemoryFileSystem fileSystem;
|
||||
|
||||
public MoveToIndexPathsTests(ITestOutputHelper output)
|
||||
: base(output)
|
||||
{
|
||||
this.fileSystem = new MemoryFileSystem();
|
||||
this.fileSystem.CreateDirectory("/c1");
|
||||
this.fileSystem.CreateFile("/a1");
|
||||
this.fileSystem.CreateFile("/b1.txt");
|
||||
this.fileSystem.CreateFile("/c1/index.md");
|
||||
this.fileSystem.CreateFile("/d1.html");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MoveAllFiles()
|
||||
{
|
||||
// Set up the operation.
|
||||
var readFiles = this.Container.Resolve<ReadFiles.Factory>();
|
||||
var op = new MoveToIndexPaths();
|
||||
|
||||
// Read and replace the paths.
|
||||
IOrderedEnumerable<string> output = readFiles(this.fileSystem)
|
||||
.Read()
|
||||
.Run(op)
|
||||
.Select(x => x.Get<UPath>().ToString())
|
||||
.OrderBy(x => x);
|
||||
|
||||
// Verify the results.
|
||||
Assert.Equal(
|
||||
new[]
|
||||
{
|
||||
"/a1",
|
||||
"/b1.txt",
|
||||
"/c1/index.md",
|
||||
"/d1/index.html",
|
||||
},
|
||||
output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OverrideCanMoveCallback()
|
||||
{
|
||||
// Set up the operation.
|
||||
var readFiles = this.Container.Resolve<ReadFiles.Factory>();
|
||||
MoveToIndexPaths? op = new MoveToIndexPaths()
|
||||
.WithCanMoveCallback((path) => path.ToString().Contains("a1"));
|
||||
|
||||
// Read and replace the paths.
|
||||
IOrderedEnumerable<string> output = readFiles(this.fileSystem)
|
||||
.Read()
|
||||
.Run(op)
|
||||
.Select(x => x.Get<UPath>().ToString())
|
||||
.OrderBy(x => x);
|
||||
|
||||
// Verify the results.
|
||||
Assert.Equal(
|
||||
new[]
|
||||
{
|
||||
"/a1/index",
|
||||
"/b1.txt",
|
||||
"/c1/index.md",
|
||||
"/d1.html",
|
||||
},
|
||||
output);
|
||||
}
|
||||
}
|
||||
}
|
29
src/Nitride.IO.Tests/Nitride.IO.Tests.csproj
Normal file
29
src/Nitride.IO.Tests/Nitride.IO.Tests.csproj
Normal file
|
@ -0,0 +1,29 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride.IO\Nitride.IO.csproj" />
|
||||
<ProjectReference Include="..\Nitride.Tests\Nitride.Tests.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Gallium" Version="1.0.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="JunitXml.TestLogger" Version="2.1.81" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Zio" Version="0.12.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
21
src/Nitride.IO.Tests/NitrideIOTestsBase.cs
Normal file
21
src/Nitride.IO.Tests/NitrideIOTestsBase.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using Autofac;
|
||||
using Nitride.Tests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Nitride.IO.Tests
|
||||
{
|
||||
public abstract class NitrideIOTestsBase : NitrideTestsBase
|
||||
{
|
||||
protected NitrideIOTestsBase(ITestOutputHelper output)
|
||||
: base(output)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void ConfigureContainer(ContainerBuilder builder)
|
||||
{
|
||||
base.ConfigureContainer(builder);
|
||||
builder.RegisterModule<NitrideIOModule>();
|
||||
}
|
||||
}
|
||||
}
|
97
src/Nitride.IO.Tests/ReadFilesTests.cs
Normal file
97
src/Nitride.IO.Tests/ReadFilesTests.cs
Normal file
|
@ -0,0 +1,97 @@
|
|||
using System.Linq;
|
||||
using Autofac;
|
||||
using Nitride.IO.Contents;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Zio;
|
||||
using Zio.FileSystems;
|
||||
|
||||
namespace Nitride.IO.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests the functionality of the ReadFiles().
|
||||
/// </summary>
|
||||
public class ReadFilesTests : NitrideIOTestsBase
|
||||
{
|
||||
private readonly MemoryFileSystem fileSystem;
|
||||
|
||||
public ReadFilesTests(ITestOutputHelper output)
|
||||
: base(output)
|
||||
{
|
||||
this.fileSystem = new MemoryFileSystem();
|
||||
this.fileSystem.CreateDirectory("/b1");
|
||||
this.fileSystem.CreateDirectory("/c1");
|
||||
this.fileSystem.CreateDirectory("/c1/c2");
|
||||
this.fileSystem.CreateDirectory("/d1");
|
||||
this.fileSystem.WriteAllText("/a.txt", "File A");
|
||||
this.fileSystem.WriteAllText("/b1/b.md", "File B");
|
||||
this.fileSystem.WriteAllText("/c1/c.txt", "File C");
|
||||
this.fileSystem.WriteAllText("/c1/c2/e.md", "File C");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadAllFiles()
|
||||
{
|
||||
// Set up the operation.
|
||||
var factory = this.Container.Resolve<ReadFiles.Factory>();
|
||||
ReadFiles op = factory(this.fileSystem);
|
||||
|
||||
// Verify the paths.
|
||||
IOrderedEnumerable<string> paths = op.Read()
|
||||
.Select(x => x.Get<UPath>())
|
||||
.Select(x => (string)x)
|
||||
.OrderBy(x => x);
|
||||
|
||||
Assert.Equal(
|
||||
new[]
|
||||
{
|
||||
"/a.txt",
|
||||
"/b1/b.md",
|
||||
"/c1/c.txt",
|
||||
"/c1/c2/e.md",
|
||||
},
|
||||
paths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadGlob()
|
||||
{
|
||||
// Set up the operation.
|
||||
var factory = this.Container.Resolve<ReadFiles.Factory>();
|
||||
ReadFiles op = factory(this.fileSystem);
|
||||
|
||||
// Verify the paths.
|
||||
IOrderedEnumerable<string> paths = op.Read("/*.txt")
|
||||
.Select(x => (string)x.Get<UPath>())
|
||||
.OrderBy(x => x);
|
||||
|
||||
Assert.Equal(
|
||||
new[]
|
||||
{
|
||||
"/a.txt",
|
||||
},
|
||||
paths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadGlobWithSubdirectories()
|
||||
{
|
||||
// Set up the operation.
|
||||
var factory = this.Container.Resolve<ReadFiles.Factory>();
|
||||
ReadFiles op = factory(this.fileSystem);
|
||||
|
||||
// Verify the paths.
|
||||
IOrderedEnumerable<string> paths = op.Read("**/*.txt")
|
||||
.Select(x => (string)x.Get<UPath>())
|
||||
.OrderBy(x => x);
|
||||
|
||||
Assert.Equal(
|
||||
new[]
|
||||
{
|
||||
"/a.txt",
|
||||
"/c1/c.txt",
|
||||
},
|
||||
paths);
|
||||
}
|
||||
}
|
||||
}
|
50
src/Nitride.IO.Tests/RemovePathPrefixTests.cs
Normal file
50
src/Nitride.IO.Tests/RemovePathPrefixTests.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using System.Linq;
|
||||
using Autofac;
|
||||
using Nitride.IO.Contents;
|
||||
using Nitride.IO.Paths;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Zio;
|
||||
using Zio.FileSystems;
|
||||
|
||||
namespace Nitride.IO.Tests
|
||||
{
|
||||
public class RemovePathPrefixTests : NitrideIOTestsBase
|
||||
{
|
||||
private readonly MemoryFileSystem fileSystem;
|
||||
|
||||
public RemovePathPrefixTests(ITestOutputHelper output)
|
||||
: base(output)
|
||||
{
|
||||
this.fileSystem = new MemoryFileSystem();
|
||||
this.fileSystem.CreateDirectory("/a");
|
||||
this.fileSystem.CreateDirectory("/a/a");
|
||||
this.fileSystem.CreateFile("/a/b1.txt");
|
||||
this.fileSystem.CreateFile("/a/a/c1.md");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PrefixAllFiles()
|
||||
{
|
||||
// Set up the operation.
|
||||
var readFiles = this.Container.Resolve<ReadFiles.Factory>();
|
||||
var op = new RemovePathPrefix("/a");
|
||||
|
||||
// Read and replace the paths.
|
||||
IOrderedEnumerable<string> output = readFiles(this.fileSystem)
|
||||
.Read()
|
||||
.Run(op)
|
||||
.Select(x => x.Get<UPath>().ToString())
|
||||
.OrderBy(x => x);
|
||||
|
||||
// Verify the results.
|
||||
Assert.Equal(
|
||||
new[]
|
||||
{
|
||||
"/a/c1.md",
|
||||
"/b1.txt",
|
||||
},
|
||||
output);
|
||||
}
|
||||
}
|
||||
}
|
63
src/Nitride.IO.Tests/WriteFilesTest.cs
Normal file
63
src/Nitride.IO.Tests/WriteFilesTest.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
using System.Linq;
|
||||
using Autofac;
|
||||
using Nitride.Contents;
|
||||
using Nitride.IO.Contents;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Zio;
|
||||
using Zio.FileSystems;
|
||||
|
||||
namespace Nitride.IO.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests the functionality of the WriteFiles().
|
||||
/// </summary>
|
||||
public class WriteFilesTest : NitrideIOTestsBase
|
||||
{
|
||||
private readonly MemoryFileSystem fileSystem;
|
||||
|
||||
public WriteFilesTest(ITestOutputHelper output)
|
||||
: base(output)
|
||||
{
|
||||
this.fileSystem = new MemoryFileSystem();
|
||||
this.fileSystem.CreateDirectory("/b1");
|
||||
this.fileSystem.CreateDirectory("/c1");
|
||||
this.fileSystem.CreateDirectory("/c1/c2");
|
||||
this.fileSystem.CreateDirectory("/d1");
|
||||
this.fileSystem.WriteAllText("/a.txt", "File A");
|
||||
this.fileSystem.WriteAllText("/b1/b.md", "File B");
|
||||
this.fileSystem.WriteAllText("/c1/c2/e.md", "File E");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteAllFiles()
|
||||
{
|
||||
// Set up the operation.
|
||||
var output = new MemoryFileSystem();
|
||||
var readFiles = this.Container.Resolve<ReadFiles.Factory>();
|
||||
var factory = this.Container.Resolve<WriteFiles.Factory>();
|
||||
WriteFiles op = factory(output);
|
||||
|
||||
// Read and write out the files. We switch one of the files to be
|
||||
// text content to make sure that works too.
|
||||
readFiles(this.fileSystem)
|
||||
.Read()
|
||||
.Select(
|
||||
x => x.Get<UPath>() == "/b1/b.md"
|
||||
? x.SetTextContent(
|
||||
((ITextContentConvertable)x.GetBinaryContent())
|
||||
.ToTextContent())
|
||||
: x)
|
||||
.Run(op);
|
||||
|
||||
// Verify the results.
|
||||
Assert.True(output.FileExists("/a.txt"));
|
||||
Assert.True(output.FileExists("/b1/b.md"));
|
||||
Assert.True(output.FileExists("/c1/c2/e.md"));
|
||||
|
||||
Assert.Equal("File A", output.ReadAllText("/a.txt"));
|
||||
Assert.Equal("File B", output.ReadAllText("/b1/b.md"));
|
||||
Assert.Equal("File E", output.ReadAllText("/c1/c2/e.md"));
|
||||
}
|
||||
}
|
||||
}
|
35
src/Nitride.IO/Contents/FileEntryBinaryContent.cs
Normal file
35
src/Nitride.IO/Contents/FileEntryBinaryContent.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using System.IO;
|
||||
using Nitride.Contents;
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.IO.Contents
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains a wrapper around a file entry to retrieve the binary data.
|
||||
/// </summary>
|
||||
public class FileEntryBinaryContent
|
||||
: IBinaryContent, ITextContentConvertable
|
||||
{
|
||||
private readonly FileEntry entry;
|
||||
|
||||
public FileEntryBinaryContent(FileEntry entry)
|
||||
{
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Stream GetStream()
|
||||
{
|
||||
return this.entry.Open(
|
||||
FileMode.Open,
|
||||
FileAccess.Read,
|
||||
FileShare.Read);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ITextContent ToTextContent()
|
||||
{
|
||||
return new FileEntryTextContent(this.entry);
|
||||
}
|
||||
}
|
||||
}
|
38
src/Nitride.IO/Contents/FileEntryTextContent.cs
Normal file
38
src/Nitride.IO/Contents/FileEntryTextContent.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
using System.IO;
|
||||
using System.Text;
|
||||
using Nitride.Contents;
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.IO.Contents
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains a wrapper around a file entry to retrieve text data.
|
||||
/// </summary>
|
||||
public class FileEntryTextContent
|
||||
: ITextContent, IBinaryContentConvertable
|
||||
{
|
||||
private readonly FileEntry entry;
|
||||
|
||||
public FileEntryTextContent(FileEntry entry)
|
||||
{
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public TextReader GetReader()
|
||||
{
|
||||
return new StreamReader(
|
||||
this.entry.Open(
|
||||
FileMode.Open,
|
||||
FileAccess.Read,
|
||||
FileShare.Read),
|
||||
Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IBinaryContent ToBinaryContent()
|
||||
{
|
||||
return new FileEntryBinaryContent(this.entry);
|
||||
}
|
||||
}
|
||||
}
|
89
src/Nitride.IO/Contents/ReadFiles.cs
Normal file
89
src/Nitride.IO/Contents/ReadFiles.cs
Normal file
|
@ -0,0 +1,89 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using DotNet.Globbing;
|
||||
using Gallium;
|
||||
using Nitride.Contents;
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.IO.Contents
|
||||
{
|
||||
/// <summary>
|
||||
/// A module that reads files from the file system and wraps them in an
|
||||
/// entity with the following components: UPath, IContent.
|
||||
/// </summary>
|
||||
public class ReadFiles : FileSystemOperation
|
||||
{
|
||||
public ReadFiles(IFileSystem fileSystem)
|
||||
: base(fileSystem)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Primary method for creating a read file.
|
||||
/// </summary>
|
||||
public delegate ReadFiles Factory(IFileSystem fileSystem);
|
||||
|
||||
/// <summary>
|
||||
/// Reads all files from the file system and returns them.
|
||||
/// </summary>
|
||||
/// <returns>A populated collection of entities.</returns>
|
||||
public IEnumerable<Entity> Read(
|
||||
UPath path = new(),
|
||||
string searchPattern = "*",
|
||||
SearchOption search = SearchOption.AllDirectories)
|
||||
{
|
||||
// Normalize the path.
|
||||
path = path == new UPath() ? "/" : path;
|
||||
|
||||
// Search for the file and wrap the results.
|
||||
IEnumerable<FileEntry> files = this.FileSystem
|
||||
.EnumerateFileEntries(path, searchPattern, search);
|
||||
IEnumerable<Entity> entities = files.Select(this.ToEntity);
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all files from the file system and returns them.
|
||||
/// </summary>
|
||||
/// <returns>A populated collection of entities.</returns>
|
||||
public IEnumerable<Entity> Read(string glob)
|
||||
{
|
||||
Glob parsed = Glob.Parse(glob);
|
||||
|
||||
return this.Read(parsed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all files from the file system, filtering them out by the
|
||||
/// minimatch pattern (as defined by DotNet.Blob).
|
||||
/// </summary>
|
||||
/// <returns>A populated collection of entities.</returns>
|
||||
public IEnumerable<Entity> Read(Glob glob)
|
||||
{
|
||||
IEnumerable<FileEntry> files = this.FileSystem
|
||||
.EnumerateFileEntries("/", "*.*", SearchOption.AllDirectories)
|
||||
.Where(x => glob.IsMatch(x.Path.ToString()));
|
||||
IEnumerable<Entity> entities = files.Select(this.ToEntity);
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an entity with the standard components for all Zio-based
|
||||
/// files. This attaches the file's path relative to the file system
|
||||
/// and a way of accessing the content from the file system.
|
||||
/// </summary>
|
||||
/// <param name="file">The Zio file entry.</param>
|
||||
/// <returns>An Entity with appropriate content.</returns>
|
||||
private Entity ToEntity(FileEntry file)
|
||||
{
|
||||
Entity entity = new Entity()
|
||||
.Set(file.Path)
|
||||
.SetBinaryContent(new FileEntryBinaryContent(file));
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
}
|
220
src/Nitride.IO/Contents/WriteFiles.cs
Normal file
220
src/Nitride.IO/Contents/WriteFiles.cs
Normal file
|
@ -0,0 +1,220 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Gallium;
|
||||
using Nitride.Contents;
|
||||
using Serilog;
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.IO.Contents
|
||||
{
|
||||
/// <summary>
|
||||
/// An operation that writes out entities to a file system.
|
||||
/// </summary>
|
||||
[WithProperties]
|
||||
public partial class WriteFiles : FileSystemOperation, INitrideOperation
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
|
||||
private Dictionary<Type, Func<IContent, Stream>> factories;
|
||||
|
||||
public WriteFiles(
|
||||
IFileSystem fileSystem,
|
||||
ILogger logger)
|
||||
: base(fileSystem)
|
||||
{
|
||||
this.logger = logger.ForContext<WriteFiles>();
|
||||
this.factories = new Dictionary<Type, Func<IContent, Stream>>
|
||||
{
|
||||
[typeof(IBinaryContent)] = GetBinaryStream,
|
||||
[typeof(ITextContent)] = this.GetTextStream,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Primary method for creating a write files operation.
|
||||
/// </summary>
|
||||
public delegate WriteFiles Factory(IFileSystem fileSystem);
|
||||
|
||||
public Dictionary<Type, Func<IContent, Stream>> StreamFactories
|
||||
{
|
||||
get => this.factories;
|
||||
set => this.factories =
|
||||
value ?? throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the encoding to force any text output.
|
||||
/// </summary>
|
||||
public Encoding? TextEncoding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Writes out all the files to the given file system using the paths
|
||||
/// currently stored in the `UPath` component. Only files that have
|
||||
/// a path and a registered writer will be written.
|
||||
/// </summary>
|
||||
/// <param name="entities">The entities to parse.</param>
|
||||
/// <returns>The same list of entities without changes.</returns>
|
||||
public IEnumerable<Entity> Run(IEnumerable<Entity> entities)
|
||||
{
|
||||
// We need the `ToList()` here, otherwise it doesn't work.
|
||||
IEnumerable<Entity> results = entities
|
||||
.ForEachEntity<UPath>(this.Process)
|
||||
.ToList();
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private static Stream GetBinaryStream(IContent content)
|
||||
{
|
||||
return ((IBinaryContent)content).GetStream();
|
||||
}
|
||||
|
||||
private Stream GetTextStream(IContent content)
|
||||
{
|
||||
// See if we can convert the stream first. If that is the case, then
|
||||
// we don't have to load it entirely in memory.
|
||||
if (content is IBinaryContentConvertable convertable)
|
||||
{
|
||||
return convertable.ToBinaryContent().GetStream();
|
||||
}
|
||||
|
||||
// We have the load the text into memory and convert it.
|
||||
var textContent = (ITextContent)content;
|
||||
string text = textContent.GetReader().ReadToEnd();
|
||||
var stream = new MemoryStream();
|
||||
var writer = new StreamWriter(
|
||||
stream,
|
||||
this.TextEncoding ?? Encoding.UTF8);
|
||||
|
||||
writer.Write(text);
|
||||
writer.Flush();
|
||||
stream.Position = 0;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method for writing out the entity. This handles the
|
||||
/// registered writers to allow for multiple `IContent` types being
|
||||
/// written out automatically.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to write out.</param>
|
||||
/// <param name="path">The path of the entity.</param>
|
||||
/// <returns>The entity passed in.</returns>
|
||||
private Entity Process(Entity entity, UPath path)
|
||||
{
|
||||
// See if we have any content. If we don't, then there is nothing
|
||||
// to do.
|
||||
if (!entity.HasContent())
|
||||
{
|
||||
return entity;
|
||||
}
|
||||
|
||||
// First see if we have a factory for the exact type of content.
|
||||
IContent content = entity.GetContent();
|
||||
|
||||
if (this.factories.TryGetValue(
|
||||
content.GetType(),
|
||||
out var getStream))
|
||||
{
|
||||
Stream stream = getStream(content);
|
||||
|
||||
return this.Process(entity, path, stream);
|
||||
}
|
||||
|
||||
// If we have an easy conversion, then use that so we don't have to
|
||||
// walk up the tree looking for one we do have.
|
||||
if (content is IBinaryContentConvertable binaryConvertable
|
||||
&& this.factories.TryGetValue(
|
||||
typeof(IBinaryContent),
|
||||
out var binaryContent))
|
||||
{
|
||||
Stream stream =
|
||||
binaryContent(binaryConvertable.ToBinaryContent());
|
||||
|
||||
return this.Process(entity, path, stream);
|
||||
}
|
||||
|
||||
if (content is ITextContentConvertable textConvertable
|
||||
&& this.factories.TryGetValue(
|
||||
typeof(ITextContent),
|
||||
out var textContent))
|
||||
{
|
||||
Stream stream = textContent(textConvertable.ToTextContent());
|
||||
|
||||
return this.Process(entity, path, stream);
|
||||
}
|
||||
|
||||
// For everything else, we have to find a content that we have a
|
||||
// registered type for by walking up the inheritance tree and
|
||||
// finding the right type.
|
||||
List<Type> types = new() { content.GetType() };
|
||||
|
||||
while (types.Count > 0)
|
||||
{
|
||||
// Check to see if we have any of these types.
|
||||
Func<IContent, Stream>? found = types
|
||||
.Select(
|
||||
x => this.factories.TryGetValue(x, out var factory)
|
||||
? factory
|
||||
: null)
|
||||
.FirstOrDefault(x => x != null);
|
||||
|
||||
if (found != null)
|
||||
{
|
||||
Stream stream = found(content);
|
||||
|
||||
return this.Process(entity, path, stream);
|
||||
}
|
||||
|
||||
// We didn't find one, so add all the parent types and try
|
||||
// again with the new list.
|
||||
types = types
|
||||
.SelectMany(
|
||||
x => new[] { x.BaseType }
|
||||
.Union(x.GetInterfaces()))
|
||||
.Where(x => x != null)
|
||||
.Select(x => x!)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
// If we got this far, we never found a content to handle.
|
||||
throw new InvalidOperationException(
|
||||
"Cannot write out entity "
|
||||
+ path
|
||||
+ " because cannot determine how to get a stream out content type "
|
||||
+ content.GetType().FullName
|
||||
+ ". To resolve, register a function to this.StreamFactories.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes out a stream to the given path in the file system.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity being written out.</param>
|
||||
/// <param name="path">The path to write out, directories will be created.</param>
|
||||
/// <param name="stream">The stream to write out.</param>
|
||||
/// <returns>The entity passed in.</returns>
|
||||
private Entity Process(Entity entity, UPath path, Stream stream)
|
||||
{
|
||||
// Make sure we have the directory structure.
|
||||
UPath directory = path.GetDirectory();
|
||||
|
||||
if (directory != "/")
|
||||
{
|
||||
this.FileSystem.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
// Write out the file.
|
||||
using Stream fileStream = this.FileSystem.CreateFile(path);
|
||||
|
||||
stream.CopyTo(fileStream);
|
||||
stream.Close();
|
||||
|
||||
// Return the entity because we've written out the files.
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
}
|
82
src/Nitride.IO/Directories/ClearDirectory.cs
Normal file
82
src/Nitride.IO/Directories/ClearDirectory.cs
Normal file
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Gallium;
|
||||
using Serilog;
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.IO.Directories
|
||||
{
|
||||
/// <summary>
|
||||
/// A Nitride operation that removes the contents of a directory but not
|
||||
/// the directory itself. This is used because some tools don't handle
|
||||
/// when the root directory is removed.
|
||||
/// This will create the top-level directory if it doesn't exist.
|
||||
/// </summary>
|
||||
[WithProperties]
|
||||
public partial class ClearDirectory : FileSystemOperation, INitrideOperation
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
|
||||
public ClearDirectory(
|
||||
ILogger logger,
|
||||
IFileSystem fileSystem)
|
||||
: base(fileSystem)
|
||||
{
|
||||
this.logger = logger.ForContext<ClearDirectory>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path of the directory to clear.
|
||||
/// </summary>
|
||||
public UPath? Path { get; set; }
|
||||
|
||||
public IEnumerable<Entity> Run()
|
||||
{
|
||||
return this.Run(new List<Entity>());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
// This really isn't an input-type of operation, but it can fit
|
||||
// inside one to keep a pattern.
|
||||
if (!this.Path.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
nameof(ClearDirectory)
|
||||
+ "cannot be used without setting the path either by the"
|
||||
+ "factory method, the constructor, the property, or "
|
||||
+ "SetPath method.");
|
||||
}
|
||||
|
||||
// See if the directory exists. If it doesn't, then we make it.
|
||||
UPath path = this.Path.Value;
|
||||
|
||||
if (!this.FileSystem.DirectoryExists(path))
|
||||
{
|
||||
this.logger.Information(
|
||||
"Creating the directory {Path}",
|
||||
path);
|
||||
this.FileSystem.CreateDirectory(path);
|
||||
}
|
||||
|
||||
// Clear out the contents.
|
||||
IEnumerable<UPath> files = this.FileSystem.EnumerateFiles(path);
|
||||
IEnumerable<UPath> directories =
|
||||
this.FileSystem.EnumerateDirectories(path);
|
||||
|
||||
foreach (UPath file in files)
|
||||
{
|
||||
this.FileSystem.DeleteFile(file);
|
||||
}
|
||||
|
||||
foreach (UPath directory in directories)
|
||||
{
|
||||
this.FileSystem.DeleteDirectory(directory, true);
|
||||
}
|
||||
|
||||
// Just pass the input on.
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
14
src/Nitride.IO/FileSystemOperation.cs
Normal file
14
src/Nitride.IO/FileSystemOperation.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Zio;
|
||||
|
||||
namespace Nitride.IO
|
||||
{
|
||||
public abstract class FileSystemOperation
|
||||
{
|
||||
protected FileSystemOperation(IFileSystem fileSystem)
|
||||
{
|
||||
this.FileSystem = fileSystem;
|
||||
}
|
||||
|
||||
protected IFileSystem FileSystem { get; }
|
||||
}
|
||||
}
|
37
src/Nitride.IO/Nitride.IO.csproj
Normal file
37
src/Nitride.IO/Nitride.IO.csproj
Normal file
|
@ -0,0 +1,37 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>Nitride.IO</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>An extension to Nitride static site generator to read and write files.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac" Version="6.2.0" />
|
||||
<PackageReference Include="DotNet.Glob" Version="3.1.2" />
|
||||
<PackageReference Include="Gallium" Version="1.0.2" />
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="Zio" Version="0.12.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride\Nitride.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Include the source generator -->
|
||||
<PropertyGroup>
|
||||
<EmitCompilerGeneratedFiles>True</EmitCompilerGeneratedFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride.Generators\Nitride.Generators.csproj">
|
||||
<OutputItemType>Analyzer</OutputItemType>
|
||||
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
14
src/Nitride.IO/NitrideIOBuilderExtensions.cs
Normal file
14
src/Nitride.IO/NitrideIOBuilderExtensions.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Autofac;
|
||||
|
||||
namespace Nitride.IO
|
||||
{
|
||||
public static class NitrideIOBuilderExtensions
|
||||
{
|
||||
public static NitrideBuilder UseIO(this NitrideBuilder builder)
|
||||
{
|
||||
return builder
|
||||
.ConfigureContainer(
|
||||
x => x.RegisterModule<NitrideIOModule>());
|
||||
}
|
||||
}
|
||||
}
|
6
src/Nitride.IO/NitrideIOModule.cs
Normal file
6
src/Nitride.IO/NitrideIOModule.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Nitride.IO
|
||||
{
|
||||
public class NitrideIOModule : NitrideModuleBase
|
||||
{
|
||||
}
|
||||
}
|
48
src/Nitride.IO/Paths/AddPathPrefix.cs
Normal file
48
src/Nitride.IO/Paths/AddPathPrefix.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Gallium;
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.IO.Paths
|
||||
{
|
||||
[WithProperties]
|
||||
public partial class AddPathPrefix : INitrideOperation
|
||||
{
|
||||
public AddPathPrefix()
|
||||
{
|
||||
}
|
||||
|
||||
public AddPathPrefix(UPath pathPrefix)
|
||||
{
|
||||
this.PathPrefix = pathPrefix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the prefix for the path operations.
|
||||
/// </summary>
|
||||
public UPath? PathPrefix { get; set; }
|
||||
|
||||
public IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
if (this.PathPrefix == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
nameof(AddPathPrefix)
|
||||
+ ".Prefix was not set "
|
||||
+ "(via constructor, property, or SetPrefix) before "
|
||||
+ "Run() was called.");
|
||||
}
|
||||
|
||||
ReplacePaths replacePaths = new ReplacePaths()
|
||||
.WithReplacement(this.RunReplacement);
|
||||
|
||||
return replacePaths.Run(input);
|
||||
}
|
||||
|
||||
private UPath RunReplacement(Entity _, UPath path)
|
||||
{
|
||||
string innerRelativePath = path.ToString().TrimStart('/');
|
||||
return this.PathPrefix!.Value / innerRelativePath;
|
||||
}
|
||||
}
|
||||
}
|
50
src/Nitride.IO/Paths/ChangePathExtension.cs
Normal file
50
src/Nitride.IO/Paths/ChangePathExtension.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Gallium;
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.IO.Paths
|
||||
{
|
||||
/// <summary>
|
||||
/// Changes the extension of the paths given.
|
||||
/// </summary>
|
||||
[WithProperties]
|
||||
public partial class ChangePathExtension : INitrideOperation
|
||||
{
|
||||
public ChangePathExtension()
|
||||
{
|
||||
}
|
||||
|
||||
public ChangePathExtension(string extension)
|
||||
{
|
||||
this.Extension = extension;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the prefix for the path operations.
|
||||
/// </summary>
|
||||
public string? Extension { get; set; }
|
||||
|
||||
public IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
if (this.Extension == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
nameof(AddPathPrefix)
|
||||
+ ".Extension was not set "
|
||||
+ "(via constructor, property, or SetExtension) before "
|
||||
+ "Run() was called.");
|
||||
}
|
||||
|
||||
ReplacePaths replacePaths = new ReplacePaths()
|
||||
.WithReplacement(this.RunReplacement);
|
||||
|
||||
return replacePaths.Run(input);
|
||||
}
|
||||
|
||||
private UPath RunReplacement(Entity _, UPath path)
|
||||
{
|
||||
return path.ChangeExtension(this.Extension!);
|
||||
}
|
||||
}
|
||||
}
|
86
src/Nitride.IO/Paths/MoveToIndexPaths.cs
Normal file
86
src/Nitride.IO/Paths/MoveToIndexPaths.cs
Normal file
|
@ -0,0 +1,86 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Gallium;
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.IO.Paths
|
||||
{
|
||||
/// <summary>
|
||||
/// Moves various files to indexes of a direction with the base filename.
|
||||
/// </summary>
|
||||
[WithProperties]
|
||||
public partial class MoveToIndexPaths : INitrideOperation
|
||||
{
|
||||
public MoveToIndexPaths()
|
||||
{
|
||||
this.CanMoveCallback = DefaultCanMoveCallback;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the callback to determine if the file should be moved.
|
||||
/// This will not be called if the file is already an index.
|
||||
/// </summary>
|
||||
public Func<UPath, bool>? CanMoveCallback { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default implement of the operation moves .html, .htm, .md, and
|
||||
/// .markdown files into their indexes.
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns>True if the file should move, otherwise false.</returns>
|
||||
public static bool DefaultCanMoveCallback(UPath path)
|
||||
{
|
||||
return path.GetExtensionWithDot() switch
|
||||
{
|
||||
".htm" => true,
|
||||
".html" => true,
|
||||
".md" => true,
|
||||
".markdown" => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
public IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
if (this.CanMoveCallback == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
nameof(MoveToIndexPaths)
|
||||
+ ".CanMoveCallback was not set "
|
||||
+ "(via constructor, property, or SetCanMoveCallback) before "
|
||||
+ "Run() was called.");
|
||||
}
|
||||
|
||||
ReplacePaths replacePaths = new ReplacePaths()
|
||||
.WithReplacement(this.RunReplacement);
|
||||
|
||||
return replacePaths.Run(input);
|
||||
}
|
||||
|
||||
private UPath RunReplacement(Entity _, UPath path)
|
||||
{
|
||||
// See if we are already an index. If that is true, then we don't
|
||||
// have to move any further.
|
||||
string? nameWithoutExtension = path.GetNameWithoutExtension();
|
||||
|
||||
if (nameWithoutExtension is null or "index")
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
// See if the path should be moved. If it can't, then just stop
|
||||
// processing.
|
||||
if (!this.CanMoveCallback!.Invoke(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
// Move the file to an index.
|
||||
UPath parent = path.GetDirectory();
|
||||
string? extension = path.GetExtensionWithDot();
|
||||
string index = "index" + extension;
|
||||
|
||||
return parent / nameWithoutExtension / index;
|
||||
}
|
||||
}
|
||||
}
|
58
src/Nitride.IO/Paths/RemovePathPrefix.cs
Normal file
58
src/Nitride.IO/Paths/RemovePathPrefix.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Gallium;
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.IO.Paths
|
||||
{
|
||||
/// <summary>
|
||||
/// An operation that removes a path prefix from the input.
|
||||
/// </summary>
|
||||
[WithProperties]
|
||||
public partial class RemovePathPrefix : INitrideOperation
|
||||
{
|
||||
public RemovePathPrefix()
|
||||
{
|
||||
}
|
||||
|
||||
public RemovePathPrefix(UPath pathPrefix)
|
||||
{
|
||||
this.PathPrefix = pathPrefix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the prefix for the path operations.
|
||||
/// </summary>
|
||||
public UPath? PathPrefix { get; set; }
|
||||
|
||||
public IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
if (this.PathPrefix == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
nameof(RemovePathPrefix)
|
||||
+ ".Prefix was not set "
|
||||
+ "(via constructor, property, or SetPrefix) before "
|
||||
+ "Replace() was called.");
|
||||
}
|
||||
|
||||
ReplacePaths replacePaths = new ReplacePaths()
|
||||
.WithReplacement(this.RunReplacement);
|
||||
|
||||
return replacePaths.Run(input);
|
||||
}
|
||||
|
||||
private UPath RunReplacement(Entity _, UPath path)
|
||||
{
|
||||
string normalized = path.ToString();
|
||||
string prefix = this.PathPrefix.ToString()!;
|
||||
|
||||
if (normalized.StartsWith(prefix))
|
||||
{
|
||||
return (UPath)path.ToString().Substring(prefix.Length);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
59
src/Nitride.IO/Paths/ReplacePaths.cs
Normal file
59
src/Nitride.IO/Paths/ReplacePaths.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Gallium;
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.IO.Paths
|
||||
{
|
||||
/// <summary>
|
||||
/// A pipeline operation that replaces the UPath of the given entity
|
||||
/// with the results of a lambda output. Entities without a path component
|
||||
/// are passed on without touching.
|
||||
/// </summary>
|
||||
[WithProperties]
|
||||
public partial class ReplacePaths : INitrideOperation
|
||||
{
|
||||
public ReplacePaths()
|
||||
{
|
||||
}
|
||||
|
||||
public ReplacePaths(Func<Entity, UPath, UPath> replacement)
|
||||
{
|
||||
this.Replacement = replacement;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the replacement callback to alter the paths.
|
||||
/// </summary>
|
||||
public Func<Entity, UPath, UPath>? Replacement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Performs the replacement on the input streams and outputs the
|
||||
/// resulting entities. Only entities that have had their paths changed
|
||||
/// will be updated, the others will be passed on as-is.
|
||||
/// </summary>
|
||||
/// <param name="input">The list of input entities.</param>
|
||||
/// <returns>The output entities.</returns>
|
||||
public IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
if (this.Replacement == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"ReplacePaths.Replacement was not set "
|
||||
+ "(via constructor, property, or SetReplacement) before "
|
||||
+ "Replace() was called.");
|
||||
}
|
||||
|
||||
return input
|
||||
.ForEachEntity<UPath>(
|
||||
(entity, oldPath) =>
|
||||
{
|
||||
UPath newPath = this.Replacement(entity, oldPath);
|
||||
|
||||
return newPath != oldPath
|
||||
? entity.Set(newPath)
|
||||
: entity;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
41
src/Nitride.IO/Paths/UPathExtensions.cs
Normal file
41
src/Nitride.IO/Paths/UPathExtensions.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using Zio;
|
||||
|
||||
namespace Nitride.IO.Paths
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for the UPath class.
|
||||
/// </summary>
|
||||
public static class UPathExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the directory path, which excludes the directory at the end
|
||||
/// of the path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to manipulate.</param>
|
||||
/// <returns>A normalized path.</returns>
|
||||
public static string GetDirectoryIndexPath(this UPath path)
|
||||
{
|
||||
if (path.GetNameWithoutExtension() == "index")
|
||||
{
|
||||
return path.GetDirectory().ToString().TrimEnd('/') + "/";
|
||||
}
|
||||
|
||||
return path.ToString();
|
||||
}
|
||||
|
||||
public static string GetParentDirectoryIndexPath(this UPath path)
|
||||
{
|
||||
UPath indexPath = path.GetDirectoryIndexPath();
|
||||
UPath parent = indexPath.GetDirectory();
|
||||
|
||||
if (parent == null!)
|
||||
{
|
||||
parent = "/";
|
||||
}
|
||||
|
||||
string parentPath = parent.ToString().TrimEnd('/') + "/";
|
||||
|
||||
return parentPath;
|
||||
}
|
||||
}
|
||||
}
|
31
src/Nitride.IO/README.md
Normal file
31
src/Nitride.IO/README.md
Normal file
|
@ -0,0 +1,31 @@
|
|||
# Nitride.IO
|
||||
|
||||
This assembly contains the primary system for reading and writing from the disk,
|
||||
along with various processes to manipulate paths. It contains three primary
|
||||
components:
|
||||
|
||||
- File System I/O
|
||||
- Path Normalization
|
||||
- Disk-Based Content
|
||||
|
||||
## File System I/O
|
||||
|
||||
Internally, this assembly uses [Zio](https://github.com/xoofx/zio), an file
|
||||
system abstraction that also has an in-memory implementation which is ideal for
|
||||
running unit tests. Zio also provides a way of treating an arbitrary directory
|
||||
as a root directory so all paths are relative to the "root".
|
||||
|
||||
## Path Normalization
|
||||
|
||||
Zio also provides `UPath`, a normalization class that handles relative and
|
||||
absolute paths. Entities that are read from the disk, such as with `ReadFiles`,
|
||||
will have a `UPath` component with the path from the file. This component is
|
||||
also used to determine the path when writing out the results.
|
||||
|
||||
## Disk-Based Content
|
||||
|
||||
This assembly also extends the `Nitride.Contents.IBinaryContent` and
|
||||
`Nitride.Contents.ITextContent` to have file system based implementations. These
|
||||
keep track of the original path and file system regardless of changes made to
|
||||
the `UPath` component.
|
||||
|
110
src/Nitride.Javascript/InstallYarn.cs
Normal file
110
src/Nitride.Javascript/InstallYarn.cs
Normal file
|
@ -0,0 +1,110 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Gallium;
|
||||
using Serilog;
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.Javascript
|
||||
{
|
||||
/// <summary>
|
||||
/// Installs Yarn in a given directory if it is missing. If the
|
||||
/// `node_modules` is already there, this step is skipped unless
|
||||
/// ForceInstall is set to true.
|
||||
/// </summary>
|
||||
[WithProperties]
|
||||
public partial class InstallYarn : INitrideOperation
|
||||
{
|
||||
private readonly IFileSystem fileSystem;
|
||||
|
||||
private readonly ILogger logger;
|
||||
|
||||
public InstallYarn(ILogger logger, IFileSystem fileSystem)
|
||||
{
|
||||
this.fileSystem = fileSystem;
|
||||
this.logger = logger.ForContext<InstallYarn>();
|
||||
this.Directory = "/";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the directory that the install should be run from. This
|
||||
/// is typically the same file as the package.json and the yarn.lock.
|
||||
/// </summary>
|
||||
public UPath? Directory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the install should be
|
||||
/// forced.
|
||||
/// </summary>
|
||||
public bool ForceInstall { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
// Make sure we have a sane value.
|
||||
if (!this.Directory.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
nameof(InstallYarn)
|
||||
+ ".Directory was not set before Run() was called.");
|
||||
}
|
||||
|
||||
// Make sure the directory exists in case we are the first.
|
||||
UPath directory = this.Directory.Value;
|
||||
|
||||
if (!this.fileSystem.DirectoryExists(directory))
|
||||
{
|
||||
this.logger.Information("Creating directory {Path}", directory);
|
||||
this.fileSystem.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
// Check to see if the directory exists.
|
||||
UPath modulesPath = directory / "node_modules";
|
||||
bool exists = this.fileSystem.DirectoryExists(modulesPath);
|
||||
|
||||
if (exists)
|
||||
{
|
||||
if (!this.ForceInstall)
|
||||
{
|
||||
this.logger.Debug(
|
||||
"{Path} already exists, skipping",
|
||||
modulesPath);
|
||||
return input;
|
||||
}
|
||||
|
||||
this.logger.Information(
|
||||
"{Path} already exists, forcing installation",
|
||||
modulesPath);
|
||||
this.fileSystem.DeleteDirectory(modulesPath, true);
|
||||
}
|
||||
|
||||
// Run Yarn install on the directory. Sadly, Yarn doesn't understand
|
||||
// a C# abstraction layer, so we need the "real" path to work with
|
||||
// these operations.
|
||||
string realPath = this.fileSystem
|
||||
.ConvertPathToInternal(directory);
|
||||
|
||||
// Run the install.
|
||||
string command = YarnHelper.GetYarnCommand();
|
||||
var start = new ProcessStartInfo
|
||||
{
|
||||
FileName = command,
|
||||
Arguments = "install",
|
||||
WorkingDirectory = realPath,
|
||||
RedirectStandardOutput = true,
|
||||
};
|
||||
Process process = Process.Start(start);
|
||||
|
||||
if (process == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Cannot start yarn install");
|
||||
}
|
||||
|
||||
process.WaitForExit();
|
||||
|
||||
// We are done, so just pass our input on.
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
33
src/Nitride.Javascript/Nitride.Javascript.csproj
Normal file
33
src/Nitride.Javascript/Nitride.Javascript.csproj
Normal file
|
@ -0,0 +1,33 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>An extension to Nitride static site generator to generate Webpack output.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride.IO\Nitride.IO.csproj" />
|
||||
<ProjectReference Include="..\Nitride\Nitride.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Gallium" Version="1.0.2" />
|
||||
<PackageReference Include="Zio" Version="0.12.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Include the source generator -->
|
||||
<PropertyGroup>
|
||||
<EmitCompilerGeneratedFiles>True</EmitCompilerGeneratedFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride.Generators\Nitride.Generators.csproj">
|
||||
<OutputItemType>Analyzer</OutputItemType>
|
||||
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
14
src/Nitride.Javascript/NitrideJavascriptBuilderExtensions.cs
Normal file
14
src/Nitride.Javascript/NitrideJavascriptBuilderExtensions.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Autofac;
|
||||
|
||||
namespace Nitride.Javascript
|
||||
{
|
||||
public static class NitrideJavascriptBuilderExtensions
|
||||
{
|
||||
public static NitrideBuilder UseJavascript(this NitrideBuilder builder)
|
||||
{
|
||||
return builder
|
||||
.ConfigureContainer(
|
||||
x => x.RegisterModule<NitrideJavascriptModule>());
|
||||
}
|
||||
}
|
||||
}
|
6
src/Nitride.Javascript/NitrideJavascriptModule.cs
Normal file
6
src/Nitride.Javascript/NitrideJavascriptModule.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Nitride.Javascript
|
||||
{
|
||||
public class NitrideJavascriptModule : NitrideModuleBase
|
||||
{
|
||||
}
|
||||
}
|
115
src/Nitride.Javascript/RunWebpack.cs
Normal file
115
src/Nitride.Javascript/RunWebpack.cs
Normal file
|
@ -0,0 +1,115 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Gallium;
|
||||
using Nitride.IO.Contents;
|
||||
using Nitride.IO.Paths;
|
||||
using Serilog;
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.Javascript
|
||||
{
|
||||
/// <summary>
|
||||
/// Runs `webpack` in the directory, writing out whatever files need to be
|
||||
/// written.
|
||||
/// </summary>
|
||||
[WithProperties]
|
||||
public partial class RunWebpack : INitrideOperation
|
||||
{
|
||||
private readonly IFileSystem fileSystem;
|
||||
|
||||
private readonly ILogger logger;
|
||||
|
||||
private readonly ReadFiles readFiles;
|
||||
|
||||
public RunWebpack(
|
||||
ILogger logger,
|
||||
IFileSystem fileSystem,
|
||||
ReadFiles readFiles)
|
||||
{
|
||||
this.fileSystem = fileSystem;
|
||||
this.readFiles = readFiles;
|
||||
this.logger = logger.ForContext<RunWebpack>();
|
||||
this.WebpackDirectory = "/";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the directory that contains the output from the
|
||||
/// webpack execution.
|
||||
/// </summary>
|
||||
public UPath? OutputDirectory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the directory that the install should be run from. This
|
||||
/// is typically the same file as the webpack.config.js.
|
||||
/// </summary>
|
||||
public UPath? WebpackDirectory { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
// Make sure we have a sane values.
|
||||
if (!this.WebpackDirectory.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
nameof(InstallYarn)
|
||||
+ ".WebpackDirectory was not set before Run() was called.");
|
||||
}
|
||||
|
||||
if (!this.OutputDirectory.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
nameof(InstallYarn)
|
||||
+ ".OutputDirectory was not set before Run() was called.");
|
||||
}
|
||||
|
||||
// Make sure the directory exists in case we are the first.
|
||||
UPath directory = this.WebpackDirectory.Value;
|
||||
|
||||
if (!this.fileSystem.DirectoryExists(directory))
|
||||
{
|
||||
this.logger.Information("Creating directory {Path}", directory);
|
||||
this.fileSystem.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
// Sadly, Yarn doesn't understand virtual file systems, so we need
|
||||
// the "real" path to the directory for this.
|
||||
string realPath = this.fileSystem
|
||||
.ConvertPathToInternal(directory);
|
||||
|
||||
// Run the install.
|
||||
this.logger.Debug("Running Webpack via Yarn");
|
||||
|
||||
string command = YarnHelper.GetYarnCommand();
|
||||
var start = new ProcessStartInfo
|
||||
{
|
||||
FileName = command,
|
||||
Arguments = "run webpack",
|
||||
WorkingDirectory = realPath,
|
||||
RedirectStandardOutput = true,
|
||||
};
|
||||
Process process = Process.Start(start);
|
||||
|
||||
if (process == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Cannot start yarn run webpack");
|
||||
}
|
||||
|
||||
process.WaitForExit();
|
||||
|
||||
// We are done and all the output will be written into the
|
||||
// filesystem, so merge it with our input.
|
||||
this.logger.Debug("Adding Webpack output to the pipeline");
|
||||
|
||||
UPath outputDirectory = this.OutputDirectory.Value;
|
||||
|
||||
return input
|
||||
.Union(
|
||||
this.readFiles
|
||||
.Read(outputDirectory)
|
||||
.Run(new RemovePathPrefix(outputDirectory)));
|
||||
}
|
||||
}
|
||||
}
|
34
src/Nitride.Javascript/YarnHelper.cs
Normal file
34
src/Nitride.Javascript/YarnHelper.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Nitride.Javascript
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class for finding how to run or install Yarn.
|
||||
/// </summary>
|
||||
public static class YarnHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the command to run Yarn.
|
||||
/// </summary>
|
||||
/// <returns>The command to run.</returns>
|
||||
public static string GetYarnCommand()
|
||||
{
|
||||
// Figure out how to run Yarn. This is needed because Windows can't
|
||||
// run the script like the shell can.
|
||||
string command = "yarn";
|
||||
string appData = Path.Combine(
|
||||
Environment.GetFolderPath(
|
||||
Environment.SpecialFolder.ApplicationData),
|
||||
"npm",
|
||||
"yarn.cmd");
|
||||
|
||||
if (File.Exists(appData))
|
||||
{
|
||||
command = appData;
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
}
|
||||
}
|
83
src/Nitride.Markdown/IdentifyMarkdown.cs
Normal file
83
src/Nitride.Markdown/IdentifyMarkdown.cs
Normal file
|
@ -0,0 +1,83 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Gallium;
|
||||
using Nitride.Contents;
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.Markdown
|
||||
{
|
||||
/// <summary>
|
||||
/// An operation that identifies Markdown files by their common extensions
|
||||
/// and converts them to text input while also adding the IsMarkdown
|
||||
/// component to identify them.
|
||||
/// </summary>
|
||||
[WithProperties]
|
||||
public partial class IdentifyMarkdown : INitrideOperation
|
||||
{
|
||||
public IdentifyMarkdown()
|
||||
{
|
||||
this.IsMarkdownTest = DefaultIsMarkdown;
|
||||
}
|
||||
|
||||
public Func<Entity, UPath, bool> IsMarkdownTest { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
return input
|
||||
.ForEachEntity<UPath, ITextContent>(
|
||||
(entity, path, _) =>
|
||||
{
|
||||
// If we aren't a Markdown file, then there is nothing
|
||||
// we can do about that.
|
||||
if (!this.IsMarkdownTest(entity, path))
|
||||
{
|
||||
return entity;
|
||||
}
|
||||
|
||||
// We are already text, so just mark it as Markdown.
|
||||
entity = entity
|
||||
.Set(IsMarkdown.Instance);
|
||||
|
||||
return entity;
|
||||
})
|
||||
.ForEachEntity<UPath, IBinaryContent>(
|
||||
(entity, path, binary) =>
|
||||
{
|
||||
// If we aren't a Markdown file, then there is nothing
|
||||
// we can do about that.
|
||||
if (!this.IsMarkdownTest(entity, path))
|
||||
{
|
||||
return entity;
|
||||
}
|
||||
|
||||
// Convert the file as a binary.
|
||||
if (binary is ITextContentConvertable textConvertable)
|
||||
{
|
||||
entity = entity
|
||||
.SetTextContent(textConvertable.ToTextContent())
|
||||
.Set(IsMarkdown.Instance);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Cannot convert a binary content to a "
|
||||
+ "text without ITextContentConvertable.");
|
||||
}
|
||||
|
||||
return entity;
|
||||
});
|
||||
}
|
||||
|
||||
private static bool DefaultIsMarkdown(Entity entity, UPath path)
|
||||
{
|
||||
return (path.GetExtensionWithDot() ?? string.Empty)
|
||||
.ToLowerInvariant() switch
|
||||
{
|
||||
".md" => true,
|
||||
".markdown" => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
10
src/Nitride.Markdown/IsMarkdown.cs
Normal file
10
src/Nitride.Markdown/IsMarkdown.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Nitride.Markdown
|
||||
{
|
||||
/// <summary>
|
||||
/// A marker class that indicates that the file is a Markdown file.
|
||||
/// </summary>
|
||||
public class IsMarkdown
|
||||
{
|
||||
public static IsMarkdown Instance { get; } = new();
|
||||
}
|
||||
}
|
48
src/Nitride.Markdown/MarkdownOperationBase.cs
Normal file
48
src/Nitride.Markdown/MarkdownOperationBase.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Gallium;
|
||||
using Markdig;
|
||||
using Nitride.Contents;
|
||||
|
||||
namespace Nitride.Markdown
|
||||
{
|
||||
public abstract class MarkdownOperationBase : INitrideOperation
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets an additional callback to configure additional features
|
||||
/// from the baseline Markdown.
|
||||
/// </summary>
|
||||
public Action<MarkdownPipelineBuilder>? ConfigureMarkdown { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
// Create the Markdown pipeline used for formatting.
|
||||
var builder = new MarkdownPipelineBuilder();
|
||||
|
||||
this.ConfigureMarkdown?.Invoke(builder);
|
||||
|
||||
MarkdownPipeline options = builder.Build();
|
||||
|
||||
// Process the Markdown files (while passing everything on).
|
||||
return input
|
||||
.ForEachEntity<IsMarkdown, ITextContent>(
|
||||
(entity, _, content) => this.Convert(
|
||||
entity,
|
||||
content,
|
||||
options));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the Markdown file into HTML.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to convert.</param>
|
||||
/// <param name="markdownContent">The content for this entity.</param>
|
||||
/// <param name="options">The markdown pipeline.</param>
|
||||
/// <returns>A converted entity.</returns>
|
||||
protected abstract Entity Convert(
|
||||
Entity entity,
|
||||
ITextContent markdownContent,
|
||||
MarkdownPipeline options);
|
||||
}
|
||||
}
|
40
src/Nitride.Markdown/MarkdownToGemtext.cs
Normal file
40
src/Nitride.Markdown/MarkdownToGemtext.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using Gallium;
|
||||
using Markdig;
|
||||
using MfGames.Markdown.Gemtext;
|
||||
using Nitride.Contents;
|
||||
using Nitride.Gemtext;
|
||||
|
||||
namespace Nitride.Markdown
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the input Markdown files into Gemtext using Markdig and
|
||||
/// MfGames.Markdown.Gemtext. This only processes files with a text input
|
||||
/// and the IsMarkdown component.
|
||||
/// </summary>
|
||||
public class MarkdownToGemtext : MarkdownOperationBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the Markdown file into HTML.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to convert.</param>
|
||||
/// <param name="markdownContent">The content for this entity.</param>
|
||||
/// <param name="options">The markdown pipeline.</param>
|
||||
/// <returns>A converted entity.</returns>
|
||||
protected override Entity Convert(
|
||||
Entity entity,
|
||||
ITextContent markdownContent,
|
||||
MarkdownPipeline options)
|
||||
{
|
||||
string markdown = markdownContent.GetText();
|
||||
string gemtext = MarkdownGemtext.ToGemtext(markdown, options);
|
||||
var content = new StringTextContent(gemtext);
|
||||
|
||||
entity = entity
|
||||
.SetTextContent(content)
|
||||
.Remove<IsMarkdown>()
|
||||
.Set(IsGemtext.Instance);
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
}
|
40
src/Nitride.Markdown/MarkdownToHtml.cs
Normal file
40
src/Nitride.Markdown/MarkdownToHtml.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using Gallium;
|
||||
using Markdig;
|
||||
using Nitride.Contents;
|
||||
using Nitride.Html;
|
||||
|
||||
namespace Nitride.Markdown
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the input Markdown files into HTML using Markdig. This only
|
||||
/// processes files with a text input and the IsMarkdown component.
|
||||
/// </summary>
|
||||
public class MarkdownToHtml : MarkdownOperationBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the Markdown file into HTML.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to convert.</param>
|
||||
/// <param name="markdownContent">The content for this entity.</param>
|
||||
/// <param name="options">The markdown pipeline.</param>
|
||||
/// <returns>A converted entity.</returns>
|
||||
protected override Entity Convert(
|
||||
Entity entity,
|
||||
ITextContent markdownContent,
|
||||
MarkdownPipeline options)
|
||||
{
|
||||
// Convert the entity to Html.
|
||||
string markdown = markdownContent.GetText();
|
||||
string html = Markdig.Markdown.ToHtml(markdown, options);
|
||||
var htmlContent = new StringTextContent(html);
|
||||
|
||||
entity = entity
|
||||
.SetTextContent(htmlContent)
|
||||
.Remove<IsMarkdown>()
|
||||
.Set(IsHtml.Instance);
|
||||
|
||||
// Return the resulting entity.
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
}
|
22
src/Nitride.Markdown/MarkdownToOperationBaseExtension.cs
Normal file
22
src/Nitride.Markdown/MarkdownToOperationBaseExtension.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using Markdig;
|
||||
|
||||
namespace Nitride.Markdown
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods to handle generics while configuring a Markdown
|
||||
/// operation.
|
||||
/// </summary>
|
||||
public static class MarkdownToOperationBaseExtension
|
||||
{
|
||||
public static T WithConfigureMarkdown<T>(
|
||||
this T operation,
|
||||
Action<MarkdownPipelineBuilder>? callback)
|
||||
where T : MarkdownOperationBase
|
||||
|
||||
{
|
||||
operation.ConfigureMarkdown = callback;
|
||||
return operation;
|
||||
}
|
||||
}
|
||||
}
|
37
src/Nitride.Markdown/Nitride.Markdown.csproj
Normal file
37
src/Nitride.Markdown/Nitride.Markdown.csproj
Normal file
|
@ -0,0 +1,37 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>An extension to Nitride static site generator to render Markdown content.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride.Gemtext\Nitride.Gemtext.csproj" />
|
||||
<ProjectReference Include="..\Nitride.Html\Nitride.Html.csproj" />
|
||||
<ProjectReference Include="..\Nitride\Nitride.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Gallium" Version="1.0.2" />
|
||||
<PackageReference Include="Markdig" Version="0.25.0" />
|
||||
<PackageReference Include="MfGames.Markdown.Gemtext" Version="1.0.0" />
|
||||
<PackageReference Include="Zio" Version="0.12.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Include the source generator -->
|
||||
<PropertyGroup>
|
||||
<EmitCompilerGeneratedFiles>True</EmitCompilerGeneratedFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride.Generators\Nitride.Generators.csproj">
|
||||
<OutputItemType>Analyzer</OutputItemType>
|
||||
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
14
src/Nitride.Markdown/NitrideMarkdownBuilderExtensions.cs
Normal file
14
src/Nitride.Markdown/NitrideMarkdownBuilderExtensions.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using Autofac;
|
||||
|
||||
namespace Nitride.Markdown
|
||||
{
|
||||
public static class NitrideMarkdownBuilderExtensions
|
||||
{
|
||||
public static NitrideBuilder UseMarkdown(this NitrideBuilder builder)
|
||||
{
|
||||
return builder
|
||||
.ConfigureContainer(
|
||||
x => x.RegisterModule<NitrideMarkdownModule>());
|
||||
}
|
||||
}
|
||||
}
|
6
src/Nitride.Markdown/NitrideMarkdownModule.cs
Normal file
6
src/Nitride.Markdown/NitrideMarkdownModule.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Nitride.Markdown
|
||||
{
|
||||
public class NitrideMarkdownModule : NitrideModuleBase
|
||||
{
|
||||
}
|
||||
}
|
15
src/Nitride.Slugs/ISlugProvider.cs
Normal file
15
src/Nitride.Slugs/ISlugProvider.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
namespace Nitride.Slugs
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface that provides slugs for various paths.
|
||||
/// </summary>
|
||||
public interface ISlugProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the given input into a slug.
|
||||
/// </summary>
|
||||
/// <param name="input">The input string to normalize.</param>
|
||||
/// <returns>The resulting slug.</returns>
|
||||
string ToSlug(string input);
|
||||
}
|
||||
}
|
31
src/Nitride.Slugs/Nitride.Slugs.csproj
Normal file
31
src/Nitride.Slugs/Nitride.Slugs.csproj
Normal file
|
@ -0,0 +1,31 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>An extension to Nitride static site generator to generate slugs.</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Slugify.Core" Version="3.0.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride\Nitride.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Include the source generator -->
|
||||
<PropertyGroup>
|
||||
<EmitCompilerGeneratedFiles>True</EmitCompilerGeneratedFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride.Generators\Nitride.Generators.csproj">
|
||||
<OutputItemType>Analyzer</OutputItemType>
|
||||
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
28
src/Nitride.Slugs/NitrideSlugsBuilderExtensions.cs
Normal file
28
src/Nitride.Slugs/NitrideSlugsBuilderExtensions.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using Autofac;
|
||||
|
||||
namespace Nitride.Slugs
|
||||
{
|
||||
public static class NitrideSlugsBuilderExtensions
|
||||
{
|
||||
public static NitrideBuilder UseSlugs(
|
||||
this NitrideBuilder builder,
|
||||
ISlugProvider slugs)
|
||||
{
|
||||
if (slugs == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(slugs));
|
||||
}
|
||||
|
||||
return builder
|
||||
.ConfigureContainer(
|
||||
x =>
|
||||
{
|
||||
x.RegisterInstance(slugs)
|
||||
.As<ISlugProvider>()
|
||||
.SingleInstance();
|
||||
x.RegisterModule<NitrideSlugsModule>();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
8
src/Nitride.Slugs/NitrideSlugsModule.cs
Normal file
8
src/Nitride.Slugs/NitrideSlugsModule.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
using Autofac;
|
||||
|
||||
namespace Nitride.Slugs
|
||||
{
|
||||
public class NitrideSlugsModule : Module
|
||||
{
|
||||
}
|
||||
}
|
78
src/Nitride.Slugs/SimpleSlugProvider.cs
Normal file
78
src/Nitride.Slugs/SimpleSlugProvider.cs
Normal file
|
@ -0,0 +1,78 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Slugify;
|
||||
|
||||
namespace Nitride.Slugs
|
||||
{
|
||||
/// <summary>
|
||||
/// A default implementation of ISlugProvider.
|
||||
/// </summary>
|
||||
public class SimpleSlugProvider : ISlugProvider, IEnumerable<string>
|
||||
{
|
||||
private readonly List<(string, string)> replacements;
|
||||
|
||||
public SimpleSlugProvider()
|
||||
{
|
||||
this.replacements = new List<(string, string)>();
|
||||
}
|
||||
|
||||
public SimpleSlugProvider(IDictionary<string, string> replacements)
|
||||
: this()
|
||||
{
|
||||
foreach (KeyValuePair<string, string> pair in replacements)
|
||||
{
|
||||
this.Add(pair.Key, pair.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a replacement for the resulting slug.
|
||||
/// </summary>
|
||||
/// <param name="search">The text to search for.</param>
|
||||
/// <param name="replace">The replacement string.</param>
|
||||
public void Add(string search, string replace)
|
||||
{
|
||||
this.replacements.Add((search, replace));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<string> GetEnumerator()
|
||||
{
|
||||
return this.replacements
|
||||
.Select(x => x.Item1)
|
||||
.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string ToSlug(string input)
|
||||
{
|
||||
// If we have null or whitespace, we have a problem.
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Cannot have a blank or null input",
|
||||
nameof(input));
|
||||
}
|
||||
|
||||
// Create a slug..
|
||||
var helper = new SlugHelper();
|
||||
string output = helper.GenerateSlug(input);
|
||||
|
||||
// Perform any additional replacements.
|
||||
foreach ((string search, string replace) in this.replacements)
|
||||
{
|
||||
output = output.Replace(search, replace);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return this.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
58
src/Nitride.Slugs/UnicodeNormalizingSlugProvider.cs
Normal file
58
src/Nitride.Slugs/UnicodeNormalizingSlugProvider.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Nitride.Slugs
|
||||
{
|
||||
/// <summary>
|
||||
/// Extends the slug provider to strip out accented characters for
|
||||
/// a normalized form.
|
||||
/// </summary>
|
||||
public class UnicodeNormalizingSlugProvider : SimpleSlugProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public UnicodeNormalizingSlugProvider()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public UnicodeNormalizingSlugProvider(
|
||||
IDictionary<string, string> replacements)
|
||||
: base(replacements)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToSlug(string input)
|
||||
{
|
||||
// If we have null or whitespace, we have a problem.
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Cannot have a blank or null input",
|
||||
nameof(input));
|
||||
}
|
||||
|
||||
// Normalize the Unicode objects.
|
||||
// Strip out the accents. This is a cheesy way of doing so.
|
||||
char[] chars = input
|
||||
.Normalize(NormalizationForm.FormD)
|
||||
.Where(this.IsNonSpacingMark)
|
||||
.ToArray();
|
||||
string normalized = new string(chars)
|
||||
.Normalize(NormalizationForm.FormC);
|
||||
|
||||
// Return the base implementation.
|
||||
return base.ToSlug(normalized);
|
||||
}
|
||||
|
||||
private bool IsNonSpacingMark(char c)
|
||||
{
|
||||
UnicodeCategory category = CharUnicodeInfo.GetUnicodeCategory(c);
|
||||
|
||||
return category != UnicodeCategory.NonSpacingMark;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue