diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 0000000..2f38f36 --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,18 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "gitversion.tool": { + "version": "5.9.0", + "commands": [ + "dotnet-gitversion" + ] + }, + "dotnet-reportgenerator-globaltool": { + "version": "5.1.3", + "commands": [ + "reportgenerator" + ] + } + } +} diff --git a/.editorconfig b/.editorconfig index fc1b7bd..d75a7f0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,122 +1,207 @@ +root = true # EditorConfig is awesome: https://EditorConfig.org -root = true - [*] -charset=utf-8 +charset = utf-8 end_of_line = lf -insert_final_newline=true -indent_style=space -indent_size=4 +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 +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_style_var_elsewhere = false:suggestion +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_preserve_single_line_statements = false +dotnet_naming_rule.event_rule.import_to_resharper = as_predefined +dotnet_naming_rule.event_rule.severity = warning +dotnet_naming_rule.event_rule.style = upper_camel_case_underscore_tolerant_style +dotnet_naming_rule.event_rule.symbols = event_symbols +dotnet_naming_rule.local_constants_rule.import_to_resharper = as_predefined +dotnet_naming_rule.local_constants_rule.severity = warning +dotnet_naming_rule.local_constants_rule.style = upper_camel_case_style +dotnet_naming_rule.local_constants_rule.symbols = local_constants_symbols +dotnet_naming_rule.private_constants_rule.import_to_resharper = as_predefined +dotnet_naming_rule.private_constants_rule.severity = warning +dotnet_naming_rule.private_constants_rule.style = upper_camel_case_style +dotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols +dotnet_naming_rule.private_instance_fields_rule.import_to_resharper = as_predefined +dotnet_naming_rule.private_instance_fields_rule.severity = warning +dotnet_naming_rule.private_instance_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.private_instance_fields_rule.symbols = private_instance_fields_symbols +dotnet_naming_rule.private_static_fields_rule.import_to_resharper = as_predefined +dotnet_naming_rule.private_static_fields_rule.resharper_style = AaBb, aaBb +dotnet_naming_rule.private_static_fields_rule.severity = warning +dotnet_naming_rule.private_static_fields_rule.style = upper_camel_case_style +dotnet_naming_rule.private_static_fields_rule.symbols = private_static_fields_symbols +dotnet_naming_rule.private_static_readonly_rule.import_to_resharper = as_predefined +dotnet_naming_rule.private_static_readonly_rule.severity = warning +dotnet_naming_rule.private_static_readonly_rule.style = upper_camel_case_style +dotnet_naming_rule.private_static_readonly_rule.symbols = private_static_readonly_symbols +dotnet_naming_style.lower_camel_case_style.capitalization = camel_case +dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case +dotnet_naming_style.upper_camel_case_underscore_tolerant_style.capitalization = pascal_case +dotnet_naming_style.upper_camel_case_underscore_tolerant_style.word_separator = _ +dotnet_naming_symbols.event_symbols.applicable_accessibilities = * +dotnet_naming_symbols.event_symbols.applicable_kinds = event +dotnet_naming_symbols.local_constants_symbols.applicable_accessibilities = * +dotnet_naming_symbols.local_constants_symbols.applicable_kinds = local +dotnet_naming_symbols.local_constants_symbols.required_modifiers = const +dotnet_naming_symbols.private_constants_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_constants_symbols.applicable_kinds = field +dotnet_naming_symbols.private_constants_symbols.required_modifiers = const +dotnet_naming_symbols.private_instance_fields_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_instance_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_fields_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static +dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static, readonly +dotnet_separate_import_directive_groups = true +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none +dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion +dotnet_style_qualification_for_event = true:suggestion +dotnet_style_qualification_for_field = true:suggestion +dotnet_style_qualification_for_method = true:suggestion +dotnet_style_qualification_for_property = true:suggestion +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion # 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_alignment_tab_fill_style = optimal_fill +resharper_align_multiline_binary_expressions_chain = false +resharper_apply_on_completion = true +resharper_autodetect_indent_settings = 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_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_continuous_indent_multiplier = 1 +resharper_cpp_alignment_tab_fill_style = optimal_fill +resharper_cpp_insert_final_newline = true +resharper_csharp_alignment_tab_fill_style = use_spaces +resharper_csharp_blank_lines_around_single_line_field = 1 +resharper_csharp_blank_lines_around_single_line_invocable = 1 +resharper_csharp_indent_style = space +resharper_csharp_insert_final_newline = true +resharper_csharp_keep_blank_lines_in_code = 1 +resharper_csharp_keep_blank_lines_in_declarations = 1 +resharper_csharp_max_line_length = 120 +resharper_csharp_naming_rule.method_property_event = AaBb_AaBb +resharper_csharp_naming_rule.private_constants = AaBb +resharper_csharp_naming_rule.private_static_fields = AaBb, aaBb +resharper_csharp_naming_rule.private_static_readonly = AaBb +resharper_csharp_stick_comment = false +resharper_csharp_wrap_arguments_style = chop_if_long +resharper_csharp_wrap_before_binary_opsign = true +resharper_csharp_wrap_extends_list_style = chop_if_long +resharper_csharp_wrap_parameters_style = chop_if_long +resharper_css_insert_final_newline = true +resharper_default_exception_variable_name = exception +resharper_enforce_line_ending_style = true +resharper_event_handler_pattern_long = On$event$ +resharper_html_alignment_tab_fill_style = optimal_fill +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_keep_existing_declaration_block_arrangement = true +resharper_keep_existing_declaration_parens_arrangement = false +resharper_keep_existing_embedded_block_arrangement = true +resharper_keep_existing_enum_arrangement = true +resharper_min_blank_lines_after_imports = 1 +resharper_nested_ternary_style = expanded +resharper_new_line_before_while = true +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 = true +resharper_qualified_using_at_nested_scope = true +resharper_resx_alignment_tab_fill_style = optimal_fill +resharper_T4_alignment_tab_fill_style = optimal_fill +resharper_t4_insert_final_newline = true +resharper_use_continuous_indent_inside_initializer_braces = false +resharper_use_indents_from_main_language_in_file = false +resharper_use_indent_from_vs = false +resharper_vb_alignment_tab_fill_style = optimal_fill +resharper_wrap_after_declaration_lpar = true +resharper_wrap_after_invocation_lpar = true +resharper_wrap_array_initializer_style = chop_if_long +resharper_wrap_before_extends_colon = true +resharper_wrap_before_first_type_parameter_constraint = true +resharper_wrap_before_type_parameter_langle = true +resharper_wrap_chained_binary_expressions = chop_if_long +resharper_wrap_chained_method_calls = chop_if_long +resharper_wrap_object_and_collection_initializer_style = chop_always +resharper_xmldoc_alignment_tab_fill_style = optimal_fill +resharper_xmldoc_indent_child_elements = ZeroIndent +resharper_xmldoc_indent_text = ZeroIndent +resharper_xmldoc_max_line_length = 80 +resharper_xml_alignment_tab_fill_style = optimal_fill # 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 +resharper_arrange_redundant_parentheses_highlighting = hint +resharper_arrange_this_qualifier_highlighting = hint +resharper_arrange_type_member_modifiers_highlighting = hint +resharper_arrange_type_modifiers_highlighting = hint +resharper_built_in_type_reference_style_for_member_access_highlighting = hint +resharper_built_in_type_reference_style_highlighting = hint +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_suggest_var_or_type_built_in_types_highlighting = hint +resharper_suggest_var_or_type_elsewhere_highlighting = hint +resharper_suggest_var_or_type_simple_types_highlighting = hint +resharper_use_null_propagation_highlighting = none +resharper_use_object_or_collection_initializer_highlighting = hint +resharper_use_string_interpolation_highlighting = hint +resharper_web_config_module_not_resolved_highlighting = warning +resharper_web_config_type_not_resolved_highlighting = warning +resharper_web_config_wrong_module_highlighting = warning +trim_trailing_whitespace = false # 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 +indent_style = space +indent_size = 2 +tab_width = 2 [*.{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 +indent_style = space +indent_size = 2 +tab_width = 2 [*.proto] -indent_style=space -indent_size=2 -tab_width=2 +indent_style = space +indent_size = 2 +tab_width = 2 + +[*.cs] +indent_style = space +indent_size = 4 +tab_width = 4 + +[*.{appxmanifest,asax,ascx,aspx,axaml,build,cg,cginc,compute,cs,cshtml,dtd,hlsl,hlsli,hlslinc,master,nuspec,paml,razor,resw,resx,skin,usf,ush,vb,xaml,xamlx,xoml,xsd}] +indent_style = space +indent_size = 4 +tab_width = 4 diff --git a/.envrc b/.envrc index 1d953f4..1b5f158 100644 --- a/.envrc +++ b/.envrc @@ -1 +1,5 @@ -use nix +# Insert our scripts into the path. +export PATH=$PWD/scripts:$PATH + +# Nix flakes are awesome. +use flake diff --git a/.gitignore b/.gitignore index d128f9b..f7ff559 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,7 @@ obj/ _ReSharper.Caches/ node_modules/ .direnv/ +coverage +TestResults/ +tests/artifacts/ +.lefthook* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 75247ea..f240c67 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,67 +1,45 @@ include: - - template: Security/SAST.gitlab-ci.yml + - template: Security/SAST.gitlab-ci.yml -# Nix handling based on https://gitlab.com/Vonfry/gitlab-ci-nix/-/blob/master/.gitlab-ci.yml stages: - build - test - release -variables: - GIT_SUBMODULE_STRATEGY: recursive - LOCAL_NIX_STORE: $CI_PROJECT_DIR/.nix/store - default: - image: nixos/nix:latest - before_script: - - "[ -f $LOCAL_NIX_STORE ] && nix-store --import < $LOCAL_NIX_STORE" - after_script: - - "[ ! -d $(dirname \"$LOCAL_NIX_STORE\") ] && mkdir -p $LOCAL_NIX_STORE" - - "[ -h ./result ] && nix-store --export $(nix-store --query --requisites --include-outputs ./result) > $LOCAL_NIX_STORE" - cache: - paths: - - .nix/ + image: registry.gitlab.com/dmoonfire/nix-flake-docker:latest build: stage: build script: - - nix-shell --run "npm install --ci" - - nix-shell --run "npx commitlint-gitlab-ci -x @commitlint/config-conventional" - - nix-shell --run "npm run build" - + - nix develop --command scripts/build.sh rules: - if: $CI_COMMIT_BRANCH -#test: -# stage: test -# script: -# - nix-shell --run "npm run test" -# # Testing is currently not working. -# #- '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 -# artifacts: -# when: always -# paths: -# - ./**/*test-result.xml -# - ./coverage/Cobertura.xml -# - ./coverage/Summary.* -# - ./**/*.nupkg -# reports: -# junit: -# - ./**/*test-result.xml -# cobertura: -# - ./coverage/Cobertura.xml +test: + stage: test + script: + - nix develop --command scripts/test.sh + artifacts: + when: always + paths: + - ./**/*test-result.xml + - ./coverage/Cobertura.xml + - ./coverage/Summary.* + - ./**/*.nupkg + reports: + junit: + - ./**/*test-result.xml + cobertura: + - ./coverage/Cobertura.xml publish: stage: release + before_script: + # Set it up so we can push the tag + - project_url=$(echo $CI_PROJECT_URL | sed 's/https:\/\///') + - git remote set-url origin https://oauth2:$GITLAB_TOKEN@$project_url script: - - nix-shell --run "npm install --ci" - - nix-shell --run "npm run build" - - nix-shell --run "npx semantic-release" + - nix develop --command scripts/release.sh rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH diff --git a/.husky/commit-msg b/.husky/commit-msg deleted file mode 100755 index e8511ea..0000000 --- a/.husky/commit-msg +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -npx --no-install commitlint --edit $1 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..f1a0e24 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +*~ +flake.* + +node_modules/ +.direnv/ +.config/ +obj/ +bin/ diff --git a/BUILDING.md b/BUILDING.md deleted file mode 100644 index 5745a36..0000000 --- a/BUILDING.md +++ /dev/null @@ -1,48 +0,0 @@ -# Building - -## Environment - -To simplify environmental setup, this project uses the following: - -- [Nix](https://nixos.org/) -- [direnv](https://direnv.net/) - -Nix does not require the full NixOS (the operating system component), just a single- or multi-user setup. `direnv` is used to automatically configure the environment when entering the directory including setting up the needed PATH and other environment variables for the build. - -### Working Without Nix - -If not using Nix to set up the environment, look at `buildInputs` in [./shell.nix](./shell.nix) which includes the tools require for building (such as a specific version of Yarn, .NET Core, and the like). - -`shell.nix` along with the data inside the `nix/` folder is considered the source of truth of dependencies and requirements for this project. - -### Node and Yarn - -Node is used as both the package metadata format (package version) and also build processes. We use [Husky]() to automatically set up Git hooks, [commitlint]() to ensure consistent check-ins that work with [semantic-release]() which automates the build process. - -Setting up the environment consists of: - -``` -npm install -``` - -## Building - -The preferred method for building is: - -``` -npm run build -``` - -This ensures everything is properly run. A short-cut can be to call the `dotnet build` directly. - -``` -dotnet build -``` - -## Testing - -Again, the preferred method of testing is: - -``` -npm test -``` diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 7d9b1bb..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,6 +0,0 @@ -# 1.0.0 (2021-09-07) - - -### Features - -* initial release ([78054ee](https://gitlab.com/mfgames-cil/nitride-cil/commit/78054ee2a72ca453cc2364427f864aca68dfae78)) diff --git a/CODE-OF-CONDUCT.md b/CODE-OF-CONDUCT.md index d33878e..5aa66ee 100644 --- a/CODE-OF-CONDUCT.md +++ b/CODE-OF-CONDUCT.md @@ -1,4 +1,3 @@ - # Contributor Covenant Code of Conduct ## Our Pledge @@ -18,24 +17,24 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall - community +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall + community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or advances of - any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email address, - without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting +- The use of sexualized language or imagery, and sexual attention or advances of + any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, + without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting ## Enforcement Responsibilities @@ -120,15 +119,14 @@ version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. +[Mozilla's code of conduct enforcement ladder][mozilla coc]. For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/faq][faq]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[Mozilla CoC]: https://github.com/mozilla/diversity -[FAQ]: https://www.contributor-covenant.org/faq +[mozilla coc]: https://github.com/mozilla/diversity +[faq]: https://www.contributor-covenant.org/faq [translations]: https://www.contributor-covenant.org/translations - diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..cba6f6a --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,660 @@ +### GNU AFFERO GENERAL PUBLIC LICENSE + +Version 3, 19 November 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +### Preamble + +The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains +free software for all its users. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + +A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + +The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + +An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing +under this license. + +The precise terms and conditions for copying, distribution and +modification follow. + +### TERMS AND CONDITIONS + +#### 0. Definitions. + +"This License" refers to version 3 of the GNU Affero General Public +License. + +"Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a "modified version" of +the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays "Appropriate Legal Notices" to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +#### 1. Source Code. + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of +a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +#### 2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes +it unnecessary. + +#### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +#### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +#### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +- a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. +- b) The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in section 4 + to "keep intact all notices". +- c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +#### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +- a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. +- b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the Corresponding + Source from a network server at no charge. +- c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. +- d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +- e) Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the general + public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +"normally used" refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +#### 7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +- a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +- b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +- c) Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +- d) Limiting the use for publicity purposes of names of licensors + or authors of the material; or +- e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or +- f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions + of it) with contractual assumptions of liability to the recipient, + for any liability that these contractual assumptions directly + impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +#### 8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +#### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +#### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +#### 11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +#### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree to +terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +#### 13. Remote Network Interaction; Use with the GNU General Public License. + +Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your +version supports such interaction) an opportunity to receive the +Corresponding Source of your version by providing access to the +Corresponding Source from a network server at no charge, through some +standard or customary means of facilitating copying of software. This +Corresponding Source shall include the Corresponding Source for any +work covered by version 3 of the GNU General Public License that is +incorporated pursuant to the following paragraph. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + +#### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU Affero General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever +published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +#### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +#### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +#### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively state +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper +mail. + +If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for +the specific requirements. + +You should also get your employer (if you work as a programmer) or +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. For more information on this, and how to apply and follow +the GNU AGPL, see . diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 02b160f..0000000 --- a/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -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. diff --git a/Nitride.sln b/Nitride.sln index 80fe061..b12eaa1 100644 --- a/Nitride.sln +++ b/Nitride.sln @@ -19,8 +19,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Html", "src\Nitride 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.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}" @@ -140,18 +138,6 @@ Global {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 - {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 @@ -269,7 +255,6 @@ Global {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} - {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} diff --git a/README.md b/README.md index f94878b..533ff0f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,19 @@ -Nitride CIL -=========== +# Nitride CIL -A static site generator based on the [Gallium ECS](https://gitlab.com/mfgames-cil/gallium-cil/). +A static site generator written in C#. + +## Entity Component System + +It is build on [Gallium ECS](https://gitlab.com/mfgames-cil/gallium-cil/), an [entity-component-system](https://en.wikipedia.org/wiki/Entity_component_system) that allows for a flexibility of adding and removing aspects of individual files including creating new ones from JSON/YAML, web servers, or the file system. Additional markers and details can be added without significant changes, allowing for new concepts that weren't envisioned by the original developers. + +## Configuration as Code + +Because it can be difficult to explain the complexities of a website with just a collection of templates and scripts, this library uses C# for the configuration. In effect, you create a program that generates a website from source files. + +## Unix Philosophy + +This library is designed with the [Unix Philosophy](https://en.wikipedia.org/wiki/Unix_philosophy): + +> Do one thing well. + +It generates website HTML from a variety of sources (mostly Markdown). The core library doesn't try to wrangle Javascript or minify CSS. It doesn't have plugins for pushing up to S3 or copy files. Those are things [Webpack](https://webpack.js.org/), [Parcel](https://parceljs.org/), `scp`, or other tools specialize in. diff --git a/TASKS.md b/TASKS.md index a501258..667acf7 100644 --- a/TASKS.md +++ b/TASKS.md @@ -1,6 +1,27 @@ # Tasks -- [ ] Does not return a proper code when failing -- [ ] CopyFiles sample does not generate data -- [ ] ReadFiles should have With methods -- [ ] WriteFiles should have With methods +- [ ] Create Nitride.Core that doesn't depend on Serilog and Autofac? + +## CLI + +- [ ] `watch` command to generate pages +- [ ] Fancy Specter-based display + +## Testing + +Write tests for the following namespaces: + +- [ ] Calendar +- [ ] Feeds +- [ ] Gemtext +- [ ] Handlebars +- [ ] Html +- [x] IO +- [ ] Markdown +- [ ] Slugs +- [ ] Temporal +- [ ] Yaml + +## Examples + +- [ ] Write more examples diff --git a/commitlint.config.js b/commitlint.config.js deleted file mode 100644 index db74d15..0000000 --- a/commitlint.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: ["@commitlint/config-conventional"], -}; diff --git a/examples/CopyFiles/CopyFiles.csproj b/examples/CopyFiles/CopyFiles.csproj index e7b804a..904f4de 100644 --- a/examples/CopyFiles/CopyFiles.csproj +++ b/examples/CopyFiles/CopyFiles.csproj @@ -2,26 +2,26 @@ Exe - net5.0 + net6.0 enable - - - + + + - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/examples/CopyFiles/CopyFilesModule.cs b/examples/CopyFiles/CopyFilesModule.cs index d14902b..b1aba44 100644 --- a/examples/CopyFiles/CopyFilesModule.cs +++ b/examples/CopyFiles/CopyFilesModule.cs @@ -1,20 +1,15 @@ using Autofac; -namespace CopyFiles +namespace CopyFiles; + +public class CopyFilesModule : Module { - public class CopyFilesModule : Module + /// + protected override void Load(ContainerBuilder builder) { - /// - protected override void Load(ContainerBuilder builder) - { - // This just registers all the non-static classes as singletons - // within the system. We use lifetimes in other components depending - // on how they are used, but in this case, we don't need it. - builder - .RegisterAssemblyTypes(this.GetType().Assembly) - .AsSelf() - .AsImplementedInterfaces() - .SingleInstance(); - } + // This just registers all the non-static classes as singletons + // within the system. We use lifetimes in other components depending + // on how they are used, but in this case, we don't need it. + builder.RegisterAssemblyTypes(this.GetType().Assembly).AsSelf().AsImplementedInterfaces().SingleInstance(); } } diff --git a/examples/CopyFiles/CopyFilesPipeline.cs b/examples/CopyFiles/CopyFilesPipeline.cs index b1d270d..ffe3f59 100644 --- a/examples/CopyFiles/CopyFilesPipeline.cs +++ b/examples/CopyFiles/CopyFilesPipeline.cs @@ -1,39 +1,106 @@ -using System; using System.Collections.Generic; using System.Threading.Tasks; using Gallium; +using Nitride; using Nitride.IO.Contents; +using Nitride.IO.Directories; +using Nitride.IO.Paths; using Nitride.Pipelines; -namespace CopyFiles +namespace CopyFiles; + +/// +/// The single pipeline used by the CopyFiles project. +/// +public class CopyFilesPipeline : PipelineBase { - /// - /// The single pipeline used by the CopyFiles project. - /// - public class CopyFilesPipeline : PipelineBase + private readonly AddPathPrefix addPathPrefix; + + private readonly ClearDirectory clearDirectory; + + private readonly ReadFiles readFiles; + + private readonly RemovePathPrefix removePathPrefix; + + private readonly WriteFiles writeFiles; + + public CopyFilesPipeline( + ReadFiles readFiles, + ClearDirectory clearDirectory, + WriteFiles writeFiles, + RemovePathPrefix removePathPrefix, + AddPathPrefix addPathPrefix) { - private readonly ReadFiles readFiles; + // While we can configure these during runtime, it seems cleaner to + // build them up during the constructor to call out the ones that + // require runtime data. + this.readFiles = readFiles.WithPattern("/input/**/*.txt"); + this.clearDirectory = clearDirectory.WithPath("/output"); + this.writeFiles = writeFiles; + this.removePathPrefix = removePathPrefix.WithPathPrefix("/input"); + this.addPathPrefix = addPathPrefix.WithPathPrefix("/output"); + } - private readonly WriteFiles writeFiles; + /// + public override Task> RunAsync(IEnumerable _) + { + // We don't care about the incoming entities which means we can + // ignore them and use the entities from the ReadFiles operation + // or we can union the entities pass in with the ReadFiles in case + // we want to merge them. + // + // This will read all the files of the given pattern and return them + // as an IEnumerable with the Zio components (UPath and + // binary contents) set. As a note, this doesn't actually read the + // file, just create a pointer to where the file could be read from. + // + // The path component will always be the relative path to the root, + // so `/input/a.txt` in this case since we only have one file. + IEnumerable entities = this.readFiles.Run(); - public CopyFilesPipeline( - ReadFiles readFiles, - WriteFiles writeFiles) - { - // While we can configure these during runtime, it seems cleaner to - // build them up during the constructor to call out the ones that - // require runtime data. - this.readFiles = readFiles; - this.writeFiles = writeFiles; - } + // Change the path. The path (Zio.UPath component) stays with the + // entity which is why there is no root directory in the writeFiles + // operation. Instead, we need to remove the `/input` and replace it + // with the `/output`. + // + // We can do that with a RemovePathPrefix followed by an + // AddPathPrefix, or use the more complex version of ChangePaths. + // In this case, we are going for easy to learn, so we'll do the + // pair. + // + // We are going to use the chain extension to make it easier to + // read. Coming out of this, we will have one entity that fulfills: + // + // entity.Get == "/output/a.txt" + entities = entities.Run(this.removePathPrefix).Run(this.addPathPrefix); - /// - public override Task> RunAsync( - IEnumerable entities) - { - throw new NotImplementedException(); - } + // Then we write out the files to the output. First we make sure we + // clear out the output. This operation performs an action when it + // it is first entered, but otherwise passes all the inputs through. + // + // We can call the clear directory in three ways. The first is to call + // the operation with: + // + // this.clearDirectory.Run() + // + // The other is to pass in the entities and get a new list of + // modified ones out. In this case, clear doesn't make any changes, + // but Entity objects are immutable, so we always work on the list + // returned. + // + // entities = this.clearDirectory.Run(entity) + // + // The third way is to use an extension on entities which lets us + // chain calls, ala Gulp's pipelines. The below code does this along + // with writing the files to the output. + entities = entities.Run(this.clearDirectory).Run(this.writeFiles); + + // If we are chaining this pipeline into another, we return the + // entities. Otherwise, we can just return an empty list. The + // pipeline is async, so it is wrapped in a task, but most + // operations are not (or are both). + return Task.FromResult(entities); } } diff --git a/examples/CopyFiles/CopyFilesProgram.cs b/examples/CopyFiles/CopyFilesProgram.cs index dbd4516..4036a91 100644 --- a/examples/CopyFiles/CopyFilesProgram.cs +++ b/examples/CopyFiles/CopyFilesProgram.cs @@ -1,6 +1,4 @@ -using System; using System.IO; -using System.Reflection; using System.Threading.Tasks; using Autofac; @@ -10,46 +8,42 @@ using MfGames.IO.Extensions; using Nitride; using Nitride.IO; -namespace CopyFiles +namespace CopyFiles; + +/// +/// Main entry point into the CopyFiles sample generator. +/// +public static class CopyFilesProgram { - /// - /// Main entry point into the CopyFiles sample generator. - /// - public static class CopyFilesProgram + public static async Task Main(string[] args) { - public static async Task Main(string[] args) - { - // All of the builder methods are fluent in that they return the - // builder to allow them to be chained. However, for documentation - // purposes, we are going to split them apart to explain the details. - var builder = new NitrideBuilder(args); + // All of the builder methods are fluent in that they return the + // builder to allow them to be chained. However, for documentation + // purposes, we are going to split them apart to explain the details. + var builder = new NitrideBuilder(args); - // Filesystem access is provided by Zio, which allows for mutliple - // folders to be combined together into a single unified file - // system. At the moment, we set the "root" directory which will - // contains all the paths, both input and output. - DirectoryInfo rootDir = typeof(CopyFilesProgram) - .GetDirectory() - !.FindGitRoot() - !.GetDirectory("examples/CopyFiles"); + // Filesystem access is provided by Zio, which allows for mutliple + // folders to be combined together into a single unified file + // system. At the moment, we set the "root" directory which will + // contains all the paths, both input and output. + DirectoryInfo rootDir = + typeof(CopyFilesProgram).GetDirectory()!.FindGitRoot()!.GetDirectory("examples/CopyFiles"); - builder.WithRootDirectory(rootDir); + builder.WithRootDirectory(rootDir); - // Like Serilog, we use a number of extension methods on the builder - // to inject functionality such as plugins and extensions. In this - // case, we only need the Nitride.IO module so we use the `UseIO` - // to inject the requisite modules and configure it. - builder.UseIO(); + // Like Serilog, we use a number of extension methods on the builder + // to inject functionality such as plugins and extensions. In this + // case, we only need the Nitride.IO module so we use the `UseIO` + // to inject the requisite modules and configure it. + builder.UseIO(); - // We use Autofac for the bulk of our registration handling. This - // was mainly because we are more comfortable with Autofac, but it - // also has a clean interface for handling some of the more esoteric - // problems we've encountered. - builder.ConfigureContainer( - x => x.RegisterModule()); + // We use Autofac for the bulk of our registration handling. This + // was mainly because we are more comfortable with Autofac, but it + // also has a clean interface for handling some of the more esoteric + // problems we've encountered. + builder.ConfigureContainer(x => x.RegisterModule()); - // Finally, we build the site generator object and run it. - return await builder.BuildAsync(); - } + // Finally, we build the site generator object and run it. + return await builder.RunAsync(); } } diff --git a/examples/CopyFiles/CopyFilesTest.cs b/examples/CopyFiles/CopyFilesTest.cs index 9514c80..07bf867 100644 --- a/examples/CopyFiles/CopyFilesTest.cs +++ b/examples/CopyFiles/CopyFilesTest.cs @@ -1,6 +1,4 @@ -using System; using System.IO; -using System.Reflection; using System.Threading.Tasks; using CliWrap; @@ -12,57 +10,47 @@ using Nitride.Tests; using Xunit; using Xunit.Abstractions; -namespace CopyFiles +namespace CopyFiles; + +/// +/// Tests the execution of the tool and ensures it is working correctly. +/// +public class CopyFilesTest : NitrideTestBase { - /// - /// Tests the execution of the tool and ensures it is working correctly. - /// - public class CopyFilesTest : NitrideTestsBase + public CopyFilesTest(ITestOutputHelper output) + : base(output) { - public CopyFilesTest(ITestOutputHelper output) - : base(output) + } + + [Fact] + public async Task Run() + { + // Figure out the paths for this test. + DirectoryInfo rootDir = + typeof(CopyFilesProgram).GetDirectory()!.FindGitRoot()!.GetDirectory("examples/CopyFiles"); + DirectoryInfo outputDir = rootDir.GetDirectory("output"); + FileInfo projectFile = rootDir.GetFile("CopyFiles.csproj"); + + this.Logger.Error("A {0}", rootDir); + + // Clear out the output directory if we have an old one. + if (outputDir.Exists) { + outputDir.Delete(true); } - [Fact] - public async Task Run() - { - // Figure out the paths for this test. - DirectoryInfo rootDir = typeof(CopyFilesProgram) - .GetDirectory() - !.FindGitRoot() - !.GetDirectory("examples/CopyFiles"); - DirectoryInfo outputDir = rootDir.GetDirectory("output"); - FileInfo projectFile = rootDir.GetFile("CopyFiles.csproj"); + // Execute the generator. This will throw if there is an exception. + await Cli.Wrap("dotnet") + .WithArguments(x => x.Add("run").Add("--project").Add(projectFile.FullName).Add("--").Add("build")) + .ExecuteAsync(); - this.Logger.Error("A {0}", rootDir); + // Make sure we have our output. + FileInfo aFile = outputDir.GetFile("a.txt"); - // Clear out the output directory if we have an old one. - if (outputDir.Exists) - { - outputDir.Delete(true); - } + Assert.True(aFile.Exists); - // Execute the generator. This will throw if there is an exception. - await Cli - .Wrap("dotnet") - .WithArguments( - x => x - .Add("run") - .Add("--project") - .Add(projectFile.FullName) - .Add("--") - .Add("build")) - .ExecuteAsync(); + string aText = aFile.ReadAllText().Trim(); - // Make sure we have our output. - FileInfo aFile = outputDir.GetFile("a.txt"); - - Assert.True(aFile.Exists); - - string aText = aFile.ReadAllText().Trim(); - - Assert.Equal("This is the 'A' file.", aText); - } + Assert.Equal("This is the 'A' file.", aText); } } diff --git a/examples/CopyFiles/README.md b/examples/CopyFiles/README.md index 62abd69..7741012 100644 --- a/examples/CopyFiles/README.md +++ b/examples/CopyFiles/README.md @@ -1,6 +1,6 @@ # Copy Files This is probably the most basic generator possible. It simply copies files from -the input and places them into the output. However, it also demonstrates a -basic setup including creating a pipeline, wiring everything up with modules, -and configuring everything. +the input and places them into the output. However, it also demonstrates a basic +setup including creating a pipeline, wiring everything up with modules, and +configuring everything. diff --git a/examples/CopyFiles/output/a.txt b/examples/CopyFiles/output/a.txt new file mode 100644 index 0000000..e8dd0e9 --- /dev/null +++ b/examples/CopyFiles/output/a.txt @@ -0,0 +1 @@ +This is the 'A' file. diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..1146b23 --- /dev/null +++ b/flake.lock @@ -0,0 +1,42 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1649676176, + "narHash": "sha256-OWKJratjt2RW151VUlJPRALb7OU2S5s+f0vLj4o1bHM=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "a4b154ebbdc88c8498a5c7b01589addc9e9cb678", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1651558728, + "narHash": "sha256-8HzyRnWlgZluUrVFNOfZAOlA1fghpOSezXvxhalGMUo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "cbe587c735b734405f56803e267820ee1559e6c1", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-unstable", + "type": "indirect" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..3de25d3 --- /dev/null +++ b/flake.nix @@ -0,0 +1,24 @@ +{ + description = "A .NET core library for easily building CLI tools"; + + inputs = { + nixpkgs.url = "nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let pkgs = nixpkgs.legacyPackages.${system}; + in { + devShell = pkgs.mkShell { + buildInputs = [ + pkgs.dotnet-sdk + pkgs.lefthook + pkgs.convco + pkgs.nodePackages.prettier + pkgs.nixfmt + pkgs.jq + ]; + }; + }); +} diff --git a/lefthook.yml b/lefthook.yml new file mode 100644 index 0000000..6fb0930 --- /dev/null +++ b/lefthook.yml @@ -0,0 +1,19 @@ +pre-push: + parallel: true + commands: + commit-check: + run: convco check + +pre-commit: + parallel: false + commands: + dotnet-format-style: + glob: "*.cs" + run: dotnet format style + dotnet-format-whitespace: + glob: "*.cs" + run: dotnet format whitespace + prettier: + run: prettier . --write --loglevel warn + nixfmt: + run: nixfmt flake.nix diff --git a/nix/sources.json b/nix/sources.json deleted file mode 100644 index 63a01f9..0000000 --- a/nix/sources.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "niv": { - "branch": "master", - "description": "Easy dependency management for Nix projects", - "homepage": "https://github.com/nmattia/niv", - "owner": "nmattia", - "repo": "niv", - "rev": "65a61b147f307d24bfd0a5cd56ce7d7b7cc61d2e", - "sha256": "17mirpsx5wyw262fpsd6n6m47jcgw8k2bwcp1iwdnrlzy4dhcgqh", - "type": "tarball", - "url": "https://github.com/nmattia/niv/archive/65a61b147f307d24bfd0a5cd56ce7d7b7cc61d2e.tar.gz", - "url_template": "https://github.com///archive/.tar.gz" - }, - "nixpkgs": { - "branch": "nixos-21.05", - "description": "Nix Packages collection", - "homepage": "", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "6c0c30146347188ce908838fd2b50c1b7db47c0c", - "sha256": "05bh6kw7jzfxpvhndsg6swgbk1x7fsgnwahzm3jgd61rsvknhn93", - "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/6c0c30146347188ce908838fd2b50c1b7db47c0c.tar.gz", - "url_template": "https://github.com///archive/.tar.gz" - } -} diff --git a/nix/sources.nix b/nix/sources.nix deleted file mode 100644 index 1938409..0000000 --- a/nix/sources.nix +++ /dev/null @@ -1,174 +0,0 @@ -# This file has been generated by Niv. - -let - - # - # The fetchers. fetch_ fetches specs of type . - # - - fetch_file = pkgs: name: spec: - let - name' = sanitizeName name + "-src"; - in - if spec.builtin or true then - builtins_fetchurl { inherit (spec) url sha256; name = name'; } - else - pkgs.fetchurl { inherit (spec) url sha256; name = name'; }; - - fetch_tarball = pkgs: name: spec: - let - name' = sanitizeName name + "-src"; - in - if spec.builtin or true then - builtins_fetchTarball { name = name'; inherit (spec) url sha256; } - else - pkgs.fetchzip { name = name'; inherit (spec) url sha256; }; - - fetch_git = name: spec: - let - ref = - if spec ? ref then spec.ref else - if spec ? branch then "refs/heads/${spec.branch}" else - if spec ? tag then "refs/tags/${spec.tag}" else - abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!"; - in - builtins.fetchGit { url = spec.repo; inherit (spec) rev; inherit ref; }; - - fetch_local = spec: spec.path; - - fetch_builtin-tarball = name: throw - ''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`. - $ niv modify ${name} -a type=tarball -a builtin=true''; - - fetch_builtin-url = name: throw - ''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`. - $ niv modify ${name} -a type=file -a builtin=true''; - - # - # Various helpers - # - - # https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695 - sanitizeName = name: - ( - concatMapStrings (s: if builtins.isList s then "-" else s) - ( - builtins.split "[^[:alnum:]+._?=-]+" - ((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name) - ) - ); - - # The set of packages used when specs are fetched using non-builtins. - mkPkgs = sources: system: - let - sourcesNixpkgs = - import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; }; - hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath; - hasThisAsNixpkgsPath = == ./.; - in - if builtins.hasAttr "nixpkgs" sources - then sourcesNixpkgs - else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then - import {} - else - abort - '' - Please specify either (through -I or NIX_PATH=nixpkgs=...) or - add a package called "nixpkgs" to your sources.json. - ''; - - # The actual fetching function. - fetch = pkgs: name: spec: - - if ! builtins.hasAttr "type" spec then - abort "ERROR: niv spec ${name} does not have a 'type' attribute" - else if spec.type == "file" then fetch_file pkgs name spec - else if spec.type == "tarball" then fetch_tarball pkgs name spec - else if spec.type == "git" then fetch_git name spec - else if spec.type == "local" then fetch_local spec - else if spec.type == "builtin-tarball" then fetch_builtin-tarball name - else if spec.type == "builtin-url" then fetch_builtin-url name - else - abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}"; - - # If the environment variable NIV_OVERRIDE_${name} is set, then use - # the path directly as opposed to the fetched source. - replace = name: drv: - let - saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name; - ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}"; - in - if ersatz == "" then drv else - # this turns the string into an actual Nix path (for both absolute and - # relative paths) - if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}"; - - # Ports of functions for older nix versions - - # a Nix version of mapAttrs if the built-in doesn't exist - mapAttrs = builtins.mapAttrs or ( - f: set: with builtins; - listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)) - ); - - # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295 - range = first: last: if first > last then [] else builtins.genList (n: first + n) (last - first + 1); - - # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257 - stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1)); - - # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269 - stringAsChars = f: s: concatStrings (map f (stringToCharacters s)); - concatMapStrings = f: list: concatStrings (map f list); - concatStrings = builtins.concatStringsSep ""; - - # https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331 - optionalAttrs = cond: as: if cond then as else {}; - - # fetchTarball version that is compatible between all the versions of Nix - builtins_fetchTarball = { url, name ? null, sha256 }@attrs: - let - inherit (builtins) lessThan nixVersion fetchTarball; - in - if lessThan nixVersion "1.12" then - fetchTarball ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; })) - else - fetchTarball attrs; - - # fetchurl version that is compatible between all the versions of Nix - builtins_fetchurl = { url, name ? null, sha256 }@attrs: - let - inherit (builtins) lessThan nixVersion fetchurl; - in - if lessThan nixVersion "1.12" then - fetchurl ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; })) - else - fetchurl attrs; - - # Create the final "sources" from the config - mkSources = config: - mapAttrs ( - name: spec: - if builtins.hasAttr "outPath" spec - then abort - "The values in sources.json should not have an 'outPath' attribute" - else - spec // { outPath = replace name (fetch config.pkgs name spec); } - ) config.sources; - - # The "config" used by the fetchers - mkConfig = - { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null - , sources ? if isNull sourcesFile then {} else builtins.fromJSON (builtins.readFile sourcesFile) - , system ? builtins.currentSystem - , pkgs ? mkPkgs sources system - }: rec { - # The sources, i.e. the attribute set of spec name to spec - inherit sources; - - # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers - inherit pkgs; - }; - -in -mkSources (mkConfig {}) // { __functor = _: settings: mkSources (mkConfig settings); } diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 8ce9cfe..0000000 --- a/package-lock.json +++ /dev/null @@ -1,12310 +0,0 @@ -{ - "name": "nitride-cil", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "nitride-cil", - "version": "1.0.0", - "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" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.12.11", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.12.11", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/highlight": { - "version": "7.10.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "node_modules/@commitlint/cli": { - "version": "13.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/format": "^13.1.0", - "@commitlint/lint": "^13.1.0", - "@commitlint/load": "^13.1.0", - "@commitlint/read": "^13.1.0", - "@commitlint/types": "^13.1.0", - "lodash": "^4.17.19", - "resolve-from": "5.0.0", - "resolve-global": "1.0.0", - "yargs": "^17.0.0" - }, - "bin": { - "commitlint": "cli.js" - }, - "engines": { - "node": ">=v12" - } - }, - "node_modules/@commitlint/config-conventional": { - "version": "13.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "conventional-changelog-conventionalcommits": "^4.3.1" - }, - "engines": { - "node": ">=v12" - } - }, - "node_modules/@commitlint/ensure": { - "version": "13.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^13.1.0", - "lodash": "^4.17.19" - }, - "engines": { - "node": ">=v12" - } - }, - "node_modules/@commitlint/execute-rule": { - "version": "13.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=v12" - } - }, - "node_modules/@commitlint/format": { - "version": "13.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^13.1.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">=v12" - } - }, - "node_modules/@commitlint/format/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@commitlint/is-ignored": { - "version": "13.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^13.1.0", - "semver": "7.3.5" - }, - "engines": { - "node": ">=v12" - } - }, - "node_modules/@commitlint/is-ignored/node_modules/semver": { - "version": "7.3.5", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@commitlint/lint": { - "version": "13.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/is-ignored": "^13.1.0", - "@commitlint/parse": "^13.1.0", - "@commitlint/rules": "^13.1.0", - "@commitlint/types": "^13.1.0" - }, - "engines": { - "node": ">=v12" - } - }, - "node_modules/@commitlint/load": { - "version": "13.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/execute-rule": "^13.0.0", - "@commitlint/resolve-extends": "^13.0.0", - "@commitlint/types": "^13.1.0", - "chalk": "^4.0.0", - "cosmiconfig": "^7.0.0", - "lodash": "^4.17.19", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=v12" - } - }, - "node_modules/@commitlint/load/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@commitlint/message": { - "version": "13.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=v12" - } - }, - "node_modules/@commitlint/parse": { - "version": "13.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/types": "^13.1.0", - "conventional-changelog-angular": "^5.0.11", - "conventional-commits-parser": "^3.0.0" - }, - "engines": { - "node": ">=v12" - } - }, - "node_modules/@commitlint/read": { - "version": "13.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/top-level": "^13.0.0", - "@commitlint/types": "^13.1.0", - "fs-extra": "^10.0.0", - "git-raw-commits": "^2.0.0" - }, - "engines": { - "node": ">=v12" - } - }, - "node_modules/@commitlint/read/node_modules/fs-extra": { - "version": "10.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@commitlint/resolve-extends": { - "version": "13.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "import-fresh": "^3.0.0", - "lodash": "^4.17.19", - "resolve-from": "^5.0.0", - "resolve-global": "^1.0.0" - }, - "engines": { - "node": ">=v12" - } - }, - "node_modules/@commitlint/rules": { - "version": "13.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/ensure": "^13.1.0", - "@commitlint/message": "^13.0.0", - "@commitlint/to-lines": "^13.0.0", - "@commitlint/types": "^13.1.0", - "execa": "^5.0.0" - }, - "engines": { - "node": ">=v12" - } - }, - "node_modules/@commitlint/to-lines": { - "version": "13.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=v12" - } - }, - "node_modules/@commitlint/top-level": { - "version": "13.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^5.0.0" - }, - "engines": { - "node": ">=v12" - } - }, - "node_modules/@commitlint/top-level/node_modules/find-up": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@commitlint/top-level/node_modules/locate-path": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@commitlint/top-level/node_modules/p-limit": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@commitlint/top-level/node_modules/p-locate": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@commitlint/top-level/node_modules/path-exists": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@commitlint/types": { - "version": "13.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0" - }, - "engines": { - "node": ">=v12" - } - }, - "node_modules/@commitlint/types/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.4", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.4", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@octokit/auth-token": { - "version": "2.4.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/types": "^6.0.0" - } - }, - "node_modules/@octokit/core": { - "version": "3.2.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.4.12", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.1.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/endpoint": { - "version": "6.0.10", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/types": "^6.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/graphql": { - "version": "4.5.8", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/request": "^5.3.0", - "@octokit/types": "^6.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/openapi-types": { - "version": "3.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "2.8.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/types": "^6.4.0" - }, - "peerDependencies": { - "@octokit/core": ">=2" - } - }, - "node_modules/@octokit/plugin-request-log": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "4.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/types": "^6.1.0", - "deprecation": "^2.3.1" - }, - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/request": { - "version": "5.4.12", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.0.0", - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.1", - "once": "^1.4.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/request-error": { - "version": "2.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/types": "^6.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "node_modules/@octokit/rest": { - "version": "18.0.12", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/core": "^3.2.3", - "@octokit/plugin-paginate-rest": "^2.6.2", - "@octokit/plugin-request-log": "^1.0.2", - "@octokit/plugin-rest-endpoint-methods": "4.4.1" - } - }, - "node_modules/@octokit/types": { - "version": "6.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/openapi-types": "^3.1.1", - "@types/node": ">= 8" - } - }, - "node_modules/@semantic-release/changelog": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@semantic-release/error": "^2.1.0", - "aggregate-error": "^3.0.0", - "fs-extra": "^9.0.0", - "lodash": "^4.17.4" - }, - "engines": { - "node": ">=10.18" - }, - "peerDependencies": { - "semantic-release": ">=15.8.0 <18.0.0" - } - }, - "node_modules/@semantic-release/commit-analyzer": { - "version": "8.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "conventional-changelog-angular": "^5.0.0", - "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.0.7", - "debug": "^4.0.0", - "import-from": "^3.0.0", - "lodash": "^4.17.4", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=10.18" - }, - "peerDependencies": { - "semantic-release": ">=16.0.0 <18.0.0" - } - }, - "node_modules/@semantic-release/error": { - "version": "2.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@semantic-release/git": { - "version": "9.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@semantic-release/error": "^2.1.0", - "aggregate-error": "^3.0.0", - "debug": "^4.0.0", - "dir-glob": "^3.0.0", - "execa": "^4.0.0", - "lodash": "^4.17.4", - "micromatch": "^4.0.0", - "p-reduce": "^2.0.0" - }, - "engines": { - "node": ">=10.18" - }, - "peerDependencies": { - "semantic-release": ">=16.0.0 <18.0.0" - } - }, - "node_modules/@semantic-release/git/node_modules/execa": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/@semantic-release/github": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/rest": "^18.0.0", - "@semantic-release/error": "^2.2.0", - "aggregate-error": "^3.0.0", - "bottleneck": "^2.18.1", - "debug": "^4.0.0", - "dir-glob": "^3.0.0", - "fs-extra": "^9.0.0", - "globby": "^11.0.0", - "http-proxy-agent": "^4.0.0", - "https-proxy-agent": "^5.0.0", - "issue-parser": "^6.0.0", - "lodash": "^4.17.4", - "mime": "^2.4.3", - "p-filter": "^2.0.0", - "p-retry": "^4.0.0", - "url-join": "^4.0.0" - }, - "engines": { - "node": ">=10.18" - }, - "peerDependencies": { - "semantic-release": ">=16.0.0 <18.0.0" - } - }, - "node_modules/@semantic-release/gitlab": { - "version": "6.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@semantic-release/error": "^2.2.0", - "aggregate-error": "^3.0.0", - "debug": "^4.0.0", - "dir-glob": "^3.0.0", - "escape-string-regexp": "^3.0.0", - "form-data": "^3.0.0", - "fs-extra": "^9.0.0", - "globby": "^11.0.0", - "got": "^10.5.2", - "lodash": "^4.17.11", - "parse-path": "^4.0.0", - "url-join": "^4.0.0" - }, - "engines": { - "node": ">=10.18" - }, - "peerDependencies": { - "semantic-release": ">=15.8.0 <18.0.0" - } - }, - "node_modules/@semantic-release/gitlab/node_modules/escape-string-regexp": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/npm": { - "version": "7.1.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@semantic-release/error": "^2.2.0", - "aggregate-error": "^3.0.0", - "execa": "^5.0.0", - "fs-extra": "^10.0.0", - "lodash": "^4.17.15", - "nerf-dart": "^1.0.0", - "normalize-url": "^6.0.0", - "npm": "^7.0.0", - "rc": "^1.2.8", - "read-pkg": "^5.0.0", - "registry-auth-token": "^4.0.0", - "semver": "^7.1.2", - "tempy": "^1.0.0" - }, - "engines": { - "node": ">=10.19" - }, - "peerDependencies": { - "semantic-release": ">=16.0.0 <18.0.0" - } - }, - "node_modules/@semantic-release/npm/node_modules/fs-extra": { - "version": "10.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@semantic-release/npm/node_modules/semver": { - "version": "7.3.4", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@semantic-release/release-notes-generator": { - "version": "9.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "conventional-changelog-angular": "^5.0.0", - "conventional-changelog-writer": "^4.0.0", - "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.0.0", - "debug": "^4.0.0", - "get-stream": "^5.0.0", - "import-from": "^3.0.0", - "into-stream": "^5.0.0", - "lodash": "^4.17.4", - "read-pkg-up": "^7.0.0" - }, - "engines": { - "node": ">=10.18" - }, - "peerDependencies": { - "semantic-release": ">=15.8.0 <18.0.0" - } - }, - "node_modules/@sindresorhus/is": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "dev": true, - "license": "MIT", - "dependencies": { - "defer-to-connect": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/@types/cacheable-request": { - "version": "6.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" - } - }, - "node_modules/@types/glob": { - "version": "7.1.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "node_modules/@types/http-cache-semantics": { - "version": "4.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/keyv": { - "version": "3.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/minimatch": { - "version": "3.0.5", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/minimist": { - "version": "1.2.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "14.14.22", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/responselike": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "dev": true, - "license": "MIT" - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ansicolors": { - "version": "0.3.2", - "dev": true, - "license": "MIT" - }, - "node_modules/argv-formatter": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/array-ify": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/array-union": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/before-after-hook": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/bottleneck": { - "version": "2.19.5", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-lookup": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/keyv": "^3.1.1", - "keyv": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cacheable-request": { - "version": "7.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-keys/node_modules/camelcase": { - "version": "5.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-keys/node_modules/map-obj": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cardinal": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" - }, - "bin": { - "cdl": "bin/cdl.js" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/chalk/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/chalk/node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-table": { - "version": "0.3.6", - "dev": true, - "dependencies": { - "colors": "1.0.3" - }, - "engines": { - "node": ">= 0.2.0" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/clone-response": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^1.0.0" - } - }, - "node_modules/clone-response/node_modules/mimic-response": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/colors": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commitlint-gitlab-ci": { - "version": "0.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@commitlint/cli": "^13.1.0" - }, - "bin": { - "commitlint-gitlab-ci": "bin/commitlint-gitlab-ci.sh" - } - }, - "node_modules/compare-func": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/conventional-changelog-angular": { - "version": "5.0.12", - "dev": true, - "license": "ISC", - "dependencies": { - "compare-func": "^2.0.0", - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-conventionalcommits": { - "version": "4.5.0", - "dev": true, - "license": "ISC", - "dependencies": { - "compare-func": "^2.0.0", - "lodash": "^4.17.15", - "q": "^1.5.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-writer": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "compare-func": "^2.0.0", - "conventional-commits-filter": "^2.0.7", - "dateformat": "^3.0.0", - "handlebars": "^4.7.6", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "semver": "^6.0.0", - "split": "^1.0.0", - "through2": "^4.0.0" - }, - "bin": { - "conventional-changelog-writer": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-changelog-writer/node_modules/readable-stream": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/conventional-changelog-writer/node_modules/semver": { - "version": "6.3.0", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/conventional-changelog-writer/node_modules/string_decoder": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/conventional-changelog-writer/node_modules/through2": { - "version": "4.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "readable-stream": "3" - } - }, - "node_modules/conventional-commits-filter": { - "version": "2.0.7", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-commits-parser": { - "version": "3.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.0.4", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^2.0.0", - "through2": "^4.0.0", - "trim-off-newlines": "^1.0.0" - }, - "bin": { - "conventional-commits-parser": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/conventional-commits-parser/node_modules/readable-stream": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/conventional-commits-parser/node_modules/safe-buffer": { - "version": "5.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/conventional-commits-parser/node_modules/split2": { - "version": "2.2.0", - "dev": true, - "license": "ISC", - "dependencies": { - "through2": "^2.0.2" - } - }, - "node_modules/conventional-commits-parser/node_modules/split2/node_modules/readable-stream": { - "version": "2.3.7", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/conventional-commits-parser/node_modules/split2/node_modules/string_decoder": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/conventional-commits-parser/node_modules/split2/node_modules/through2": { - "version": "2.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/conventional-commits-parser/node_modules/string_decoder": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/conventional-commits-parser/node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/conventional-commits-parser/node_modules/through2": { - "version": "4.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "readable-stream": "3" - } - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/cosmiconfig": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/dargs": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/dateformat": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/debug": { - "version": "4.3.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/decamelize": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/decompress-response": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-response": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/del": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/del/node_modules/p-map": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/del/node_modules/rimraf": { - "version": "3.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/deprecation": { - "version": "2.3.1", - "dev": true, - "license": "ISC" - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dot-prop": { - "version": "5.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/duplexer2": { - "version": "0.1.4", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "readable-stream": "^2.0.2" - } - }, - "node_modules/duplexer3": { - "version": "0.1.4", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "dev": true, - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/env-ci": { - "version": "5.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^4.0.0", - "java-properties": "^1.0.0" - }, - "engines": { - "node": ">=10.13" - } - }, - "node_modules/env-ci/node_modules/execa": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execa/node_modules/get-stream": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/execa/node_modules/human-signals": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/fast-glob": { - "version": "3.2.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fastq": { - "version": "1.10.0", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/filter-obj": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/find-up": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/find-versions": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "semver-regex": "^3.1.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/form-data": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/from2": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "node_modules/fs-extra": { - "version": "9.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/function-bind": { - "version": "1.1.1", - "dev": true, - "license": "MIT" - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-stream": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-log-parser": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "argv-formatter": "~1.0.0", - "spawn-error-forwarder": "~1.0.0", - "split2": "~1.0.0", - "stream-combiner2": "~1.1.1", - "through2": "~2.0.0", - "traverse": "~0.6.6" - } - }, - "node_modules/git-raw-commits": { - "version": "2.0.9", - "dev": true, - "license": "MIT", - "dependencies": { - "dargs": "^7.0.0", - "lodash.template": "^4.0.2", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" - }, - "bin": { - "git-raw-commits": "cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/git-raw-commits/node_modules/readable-stream": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/git-raw-commits/node_modules/split2": { - "version": "3.2.2", - "dev": true, - "license": "ISC", - "dependencies": { - "readable-stream": "^3.0.0" - } - }, - "node_modules/git-raw-commits/node_modules/string_decoder": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/git-raw-commits/node_modules/through2": { - "version": "4.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "readable-stream": "3" - } - }, - "node_modules/glob": { - "version": "7.1.7", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.1", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-promise": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/glob": "^7.1.3" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "type": "individual", - "url": "https://github.com/sponsors/ahmadnassri" - }, - "peerDependencies": { - "glob": "^7.1.6" - } - }, - "node_modules/global-dirs": { - "version": "0.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "^1.3.4" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/globby": { - "version": "11.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/got": { - "version": "10.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^2.0.0", - "@szmarczak/http-timer": "^4.0.0", - "@types/cacheable-request": "^6.0.1", - "cacheable-lookup": "^2.0.0", - "cacheable-request": "^7.0.1", - "decompress-response": "^5.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^5.0.0", - "lowercase-keys": "^2.0.0", - "mimic-response": "^2.1.0", - "p-cancelable": "^2.0.0", - "p-event": "^4.0.0", - "responselike": "^2.0.0", - "to-readable-stream": "^2.0.0", - "type-fest": "^0.10.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/got/node_modules/type-fest": { - "version": "0.10.0", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.4", - "dev": true, - "license": "ISC" - }, - "node_modules/handlebars": { - "version": "4.7.6", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hook-std": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.8", - "dev": true, - "license": "ISC" - }, - "node_modules/http-cache-semantics": { - "version": "4.1.0", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-agent/node_modules/agent-base": { - "version": "6.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent/node_modules/agent-base": { - "version": "6.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/human-signals": { - "version": "1.1.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8.12.0" - } - }, - "node_modules/husky": { - "version": "7.0.2", - "dev": true, - "license": "MIT", - "bin": { - "husky": "lib/bin.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "node_modules/ignore": { - "version": "5.1.8", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/import-from": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "dev": true, - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "dev": true, - "license": "ISC" - }, - "node_modules/into-stream": { - "version": "5.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "from2": "^2.3.0", - "p-is-promise": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "dev": true, - "license": "MIT" - }, - "node_modules/is-core-module": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-obj": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-ssh": { - "version": "1.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "protocols": "^1.1.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-text-path": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "text-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/issue-parser": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash.capitalize": "^4.2.1", - "lodash.escaperegexp": "^4.1.2", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.uniqby": "^4.7.0" - }, - "engines": { - "node": ">=10.13" - } - }, - "node_modules/java-properties": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "dev": true, - "license": "ISC" - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonparse": { - "version": "1.3.1", - "dev": true, - "engines": [ - "node >= 0.2.0" - ], - "license": "MIT" - }, - "node_modules/JSONStream": { - "version": "1.3.5", - "dev": true, - "license": "(MIT OR Apache-2.0)", - "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/keyv": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.1.6", - "dev": true, - "license": "MIT" - }, - "node_modules/load-json-file": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/pify": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/locate-path": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash._reinterpolate": { - "version": "3.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.capitalize": { - "version": "4.2.1", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.escaperegexp": { - "version": "4.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.ismatch": { - "version": "4.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.template": { - "version": "4.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash._reinterpolate": "^3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "node_modules/lodash.templatesettings": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash._reinterpolate": "^3.0.0" - } - }, - "node_modules/lodash.uniqby": { - "version": "4.7.0", - "dev": true, - "license": "MIT" - }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/map-obj": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/marked": { - "version": "2.1.3", - "dev": true, - "license": "MIT", - "bin": { - "marked": "bin/marked" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/marked-terminal": { - "version": "4.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.3.1", - "cardinal": "^2.1.1", - "chalk": "^4.1.0", - "cli-table": "^0.3.1", - "node-emoji": "^1.10.0", - "supports-hyperlinks": "^2.1.0" - }, - "peerDependencies": { - "marked": "^1.0.0 || ^2.0.0" - } - }, - "node_modules/marked-terminal/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/meow": { - "version": "8.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/hosted-git-info": { - "version": "3.0.7", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/meow/node_modules/normalize-package-data": { - "version": "3.0.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^3.0.6", - "resolve": "^1.17.0", - "semver": "^7.3.2", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/meow/node_modules/semver": { - "version": "7.3.4", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/meow/node_modules/type-fest": { - "version": "0.18.1", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mime": { - "version": "2.5.0", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.49.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.32", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.49.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.0.4", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.5", - "dev": true, - "license": "MIT" - }, - "node_modules/minimist-options": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/modify-values": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "dev": true, - "license": "MIT" - }, - "node_modules/nerf-dart": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/node-emoji": { - "version": "1.11.0", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.21" - } - }, - "node_modules/node-fetch": { - "version": "2.6.1", - "dev": true, - "license": "MIT", - "engines": { - "node": "4.x || >=6.0.0" - } - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm": { - "version": "7.22.0", - "bundleDependencies": [ - "@npmcli/arborist", - "@npmcli/ci-detect", - "@npmcli/config", - "@npmcli/map-workspaces", - "@npmcli/package-json", - "@npmcli/run-script", - "abbrev", - "ansicolors", - "ansistyles", - "archy", - "cacache", - "chalk", - "chownr", - "cli-columns", - "cli-table3", - "columnify", - "fastest-levenshtein", - "glob", - "graceful-fs", - "hosted-git-info", - "ini", - "init-package-json", - "is-cidr", - "json-parse-even-better-errors", - "libnpmaccess", - "libnpmdiff", - "libnpmexec", - "libnpmfund", - "libnpmhook", - "libnpmorg", - "libnpmpack", - "libnpmpublish", - "libnpmsearch", - "libnpmteam", - "libnpmversion", - "make-fetch-happen", - "minipass", - "minipass-pipeline", - "mkdirp", - "mkdirp-infer-owner", - "ms", - "node-gyp", - "nopt", - "npm-audit-report", - "npm-package-arg", - "npm-pick-manifest", - "npm-profile", - "npm-registry-fetch", - "npm-user-validate", - "npmlog", - "opener", - "pacote", - "parse-conflict-json", - "qrcode-terminal", - "read", - "read-package-json", - "read-package-json-fast", - "readdir-scoped-modules", - "rimraf", - "semver", - "ssri", - "tar", - "text-table", - "tiny-relative-date", - "treeverse", - "validate-npm-package-name", - "which", - "write-file-atomic", - "@gar/promisify", - "@npmcli/disparity-colors", - "@npmcli/fs", - "@npmcli/git", - "@npmcli/installed-package-contents", - "@npmcli/metavuln-calculator", - "@npmcli/move-file", - "@npmcli/name-from-folder", - "@npmcli/node-gyp", - "@npmcli/promise-spawn", - "@tootallnate/once", - "agent-base", - "agentkeepalive", - "aggregate-error", - "ajv", - "ansi-regex", - "ansi-styles", - "aproba", - "are-we-there-yet", - "asap", - "asn1", - "assert-plus", - "asynckit", - "aws-sign2", - "aws4", - "balanced-match", - "bcrypt-pbkdf", - "bin-links", - "binary-extensions", - "brace-expansion", - "builtins", - "caseless", - "cidr-regex", - "clean-stack", - "clone", - "cmd-shim", - "code-point-at", - "color-convert", - "color-name", - "color-support", - "colors", - "combined-stream", - "common-ancestor-path", - "concat-map", - "console-control-strings", - "core-util-is", - "dashdash", - "debug", - "debuglog", - "defaults", - "delayed-stream", - "delegates", - "depd", - "dezalgo", - "diff", - "ecc-jsbn", - "emoji-regex", - "encoding", - "env-paths", - "err-code", - "extend", - "extsprintf", - "fast-deep-equal", - "fast-json-stable-stringify", - "forever-agent", - "fs-minipass", - "fs.realpath", - "function-bind", - "gauge", - "getpass", - "har-schema", - "har-validator", - "has", - "has-flag", - "has-unicode", - "http-cache-semantics", - "http-proxy-agent", - "http-signature", - "https-proxy-agent", - "humanize-ms", - "iconv-lite", - "ignore-walk", - "imurmurhash", - "indent-string", - "infer-owner", - "inflight", - "inherits", - "ip", - "ip-regex", - "is-core-module", - "is-fullwidth-code-point", - "is-lambda", - "is-typedarray", - "isexe", - "isstream", - "jsbn", - "json-schema", - "json-schema-traverse", - "json-stringify-nice", - "json-stringify-safe", - "jsonparse", - "jsprim", - "just-diff", - "just-diff-apply", - "lru-cache", - "mime-db", - "mime-types", - "minimatch", - "minipass-collect", - "minipass-fetch", - "minipass-flush", - "minipass-json-stream", - "minipass-sized", - "minizlib", - "mute-stream", - "negotiator", - "normalize-package-data", - "npm-bundled", - "npm-install-checks", - "npm-normalize-package-bin", - "npm-packlist", - "number-is-nan", - "oauth-sign", - "object-assign", - "once", - "p-map", - "path-is-absolute", - "performance-now", - "proc-log", - "promise-all-reject-late", - "promise-call-limit", - "promise-inflight", - "promise-retry", - "promzard", - "psl", - "punycode", - "qs", - "read-cmd-shim", - "readable-stream", - "request", - "retry", - "safe-buffer", - "safer-buffer", - "set-blocking", - "signal-exit", - "smart-buffer", - "socks", - "socks-proxy-agent", - "spdx-correct", - "spdx-exceptions", - "spdx-expression-parse", - "spdx-license-ids", - "sshpk", - "string_decoder", - "string-width", - "stringify-package", - "strip-ansi", - "supports-color", - "tunnel-agent", - "tweetnacl", - "typedarray-to-buffer", - "unique-filename", - "unique-slug", - "uri-js", - "util-deprecate", - "uuid", - "validate-npm-package-license", - "verror", - "walk-up-path", - "wcwidth", - "wide-align", - "wrappy", - "yallist" - ], - "dev": true, - "license": "Artistic-2.0", - "workspaces": [ - "docs", - "packages/*" - ], - "dependencies": { - "@npmcli/arborist": "^2.8.3", - "@npmcli/ci-detect": "^1.2.0", - "@npmcli/config": "^2.3.0", - "@npmcli/map-workspaces": "^1.0.4", - "@npmcli/package-json": "^1.0.1", - "@npmcli/run-script": "^1.8.6", - "abbrev": "~1.1.1", - "ansicolors": "~0.3.2", - "ansistyles": "~0.1.3", - "archy": "~1.0.0", - "cacache": "^15.3.0", - "chalk": "^4.1.2", - "chownr": "^2.0.0", - "cli-columns": "^3.1.2", - "cli-table3": "^0.6.0", - "columnify": "~1.5.4", - "fastest-levenshtein": "^1.0.12", - "glob": "^7.1.7", - "graceful-fs": "^4.2.8", - "hosted-git-info": "^4.0.2", - "ini": "^2.0.0", - "init-package-json": "^2.0.4", - "is-cidr": "^4.0.2", - "json-parse-even-better-errors": "^2.3.1", - "libnpmaccess": "^4.0.2", - "libnpmdiff": "^2.0.4", - "libnpmexec": "^2.0.1", - "libnpmfund": "^1.1.0", - "libnpmhook": "^6.0.2", - "libnpmorg": "^2.0.2", - "libnpmpack": "^2.0.1", - "libnpmpublish": "^4.0.1", - "libnpmsearch": "^3.1.1", - "libnpmteam": "^2.0.3", - "libnpmversion": "^1.2.1", - "make-fetch-happen": "^9.1.0", - "minipass": "^3.1.3", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "ms": "^2.1.2", - "node-gyp": "^7.1.2", - "nopt": "^5.0.0", - "npm-audit-report": "^2.1.5", - "npm-package-arg": "^8.1.5", - "npm-pick-manifest": "^6.1.1", - "npm-profile": "^5.0.3", - "npm-registry-fetch": "^11.0.0", - "npm-user-validate": "^1.0.1", - "npmlog": "^5.0.1", - "opener": "^1.5.2", - "pacote": "^11.3.5", - "parse-conflict-json": "^1.1.1", - "qrcode-terminal": "^0.12.0", - "read": "~1.0.7", - "read-package-json": "^4.1.1", - "read-package-json-fast": "^2.0.3", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "ssri": "^8.0.1", - "tar": "^6.1.11", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", - "treeverse": "^1.0.4", - "validate-npm-package-name": "~3.0.0", - "which": "^2.0.2", - "write-file-atomic": "^3.0.3" - }, - "bin": { - "npm": "bin/npm-cli.js", - "npx": "bin/npx-cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/@gar/promisify": { - "version": "1.1.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "2.8.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/map-workspaces": "^1.0.2", - "@npmcli/metavuln-calculator": "^1.1.0", - "@npmcli/move-file": "^1.1.0", - "@npmcli/name-from-folder": "^1.0.1", - "@npmcli/node-gyp": "^1.0.1", - "@npmcli/package-json": "^1.0.1", - "@npmcli/run-script": "^1.8.2", - "bin-links": "^2.2.1", - "cacache": "^15.0.3", - "common-ancestor-path": "^1.0.1", - "json-parse-even-better-errors": "^2.3.1", - "json-stringify-nice": "^1.1.4", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "npm-install-checks": "^4.0.0", - "npm-package-arg": "^8.1.5", - "npm-pick-manifest": "^6.1.0", - "npm-registry-fetch": "^11.0.0", - "pacote": "^11.3.5", - "parse-conflict-json": "^1.1.1", - "proc-log": "^1.0.0", - "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^1.0.1", - "read-package-json-fast": "^2.0.2", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "ssri": "^8.0.1", - "treeverse": "^1.0.4", - "walk-up-path": "^1.0.0" - }, - "bin": { - "arborist": "bin/index.js" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/@npmcli/ci-detect": { - "version": "1.3.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/@npmcli/config": { - "version": "2.3.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "ini": "^2.0.0", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^5.0.0", - "semver": "^7.3.4", - "walk-up-path": "^1.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/@npmcli/disparity-colors": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "ansi-styles": "^4.3.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/@npmcli/fs": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@gar/promisify": "^1.0.1", - "semver": "^7.3.5" - } - }, - "node_modules/npm/node_modules/@npmcli/git": { - "version": "2.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/promise-spawn": "^1.3.2", - "lru-cache": "^6.0.0", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^6.1.1", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^2.0.2" - } - }, - "node_modules/npm/node_modules/@npmcli/installed-package-contents": { - "version": "1.0.7", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "installed-package-contents": "index.js" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "1.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/name-from-folder": "^1.0.1", - "glob": "^7.1.6", - "minimatch": "^3.0.4", - "read-package-json-fast": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "1.1.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "cacache": "^15.0.5", - "pacote": "^11.1.11", - "semver": "^7.3.2" - } - }, - "node_modules/npm/node_modules/@npmcli/move-file": { - "version": "1.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/@npmcli/name-from-folder": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/@npmcli/node-gyp": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^2.3.1" - } - }, - "node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "1.3.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "infer-owner": "^1.0.4" - } - }, - "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "1.8.6", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/node-gyp": "^1.0.2", - "@npmcli/promise-spawn": "^1.3.2", - "node-gyp": "^7.1.0", - "read-package-json-fast": "^2.0.1" - } - }, - "node_modules/npm/node_modules/@tootallnate/once": { - "version": "1.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/npm/node_modules/abbrev": { - "version": "1.1.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/agent-base": { - "version": "6.0.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/npm/node_modules/agentkeepalive": { - "version": "4.1.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/npm/node_modules/aggregate-error": { - "version": "3.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/ajv": { - "version": "6.12.6", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/npm/node_modules/ansi-regex": { - "version": "2.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/npm/node_modules/ansicolors": { - "version": "0.3.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/ansistyles": { - "version": "0.1.3", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/aproba": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/archy": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/are-we-there-yet": { - "version": "1.1.6", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/asap": { - "version": "2.0.6", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/asn1": { - "version": "0.2.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/npm/node_modules/assert-plus": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/npm/node_modules/asynckit": { - "version": "0.4.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/aws-sign2": { - "version": "0.7.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/npm/node_modules/aws4": { - "version": "1.11.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/npm/node_modules/bin-links": { - "version": "2.2.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "cmd-shim": "^4.0.1", - "mkdirp": "^1.0.3", - "npm-normalize-package-bin": "^1.0.0", - "read-cmd-shim": "^2.0.0", - "rimraf": "^3.0.0", - "write-file-atomic": "^3.0.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/binary-extensions": { - "version": "2.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/npm/node_modules/builtins": { - "version": "1.0.3", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/cacache": { - "version": "15.3.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/caseless": { - "version": "0.12.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0" - }, - "node_modules/npm/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/npm/node_modules/chownr": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/cidr-regex": { - "version": "3.1.1", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "ip-regex": "^4.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/clean-stack": { - "version": "2.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/npm/node_modules/cli-columns": { - "version": "3.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "string-width": "^2.0.0", - "strip-ansi": "^3.0.1" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/npm/node_modules/cli-table3": { - "version": "0.6.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "object-assign": "^4.1.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "colors": "^1.1.2" - } - }, - "node_modules/npm/node_modules/cli-table3/node_modules/ansi-regex": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/cli-table3/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/cli-table3/node_modules/string-width": { - "version": "4.2.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/cli-table3/node_modules/strip-ansi": { - "version": "6.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/clone": { - "version": "1.0.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/npm/node_modules/cmd-shim": { - "version": "4.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "mkdirp-infer-owner": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/code-point-at": { - "version": "1.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/npm/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/color-support": { - "version": "1.1.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/npm/node_modules/colors": { - "version": "1.4.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/npm/node_modules/columnify": { - "version": "1.5.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "strip-ansi": "^3.0.0", - "wcwidth": "^1.0.0" - } - }, - "node_modules/npm/node_modules/combined-stream": { - "version": "1.0.8", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/npm/node_modules/common-ancestor-path": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/console-control-strings": { - "version": "1.1.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/core-util-is": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/dashdash": { - "version": "1.14.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/npm/node_modules/debug": { - "version": "4.3.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/npm/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/debuglog": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/npm/node_modules/defaults": { - "version": "1.0.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - } - }, - "node_modules/npm/node_modules/delayed-stream": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/npm/node_modules/delegates": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/depd": { - "version": "1.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/npm/node_modules/dezalgo": { - "version": "1.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/npm/node_modules/diff": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/npm/node_modules/ecc-jsbn": { - "version": "0.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/npm/node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/encoding": { - "version": "0.1.13", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/npm/node_modules/env-paths": { - "version": "2.2.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/npm/node_modules/err-code": { - "version": "2.0.3", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/extend": { - "version": "3.0.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/extsprintf": { - "version": "1.3.0", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/fast-deep-equal": { - "version": "3.1.3", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/fastest-levenshtein": { - "version": "1.0.12", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/forever-agent": { - "version": "0.6.1", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/npm/node_modules/fs-minipass": { - "version": "2.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/fs.realpath": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/function-bind": { - "version": "1.1.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/gauge": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1 || ^2.0.0", - "strip-ansi": "^3.0.1 || ^4.0.0", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/getpass": { - "version": "0.1.7", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/npm/node_modules/glob": { - "version": "7.1.7", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/graceful-fs": { - "version": "4.2.8", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/har-schema": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=4" - } - }, - "node_modules/npm/node_modules/har-validator": { - "version": "5.1.5", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/npm/node_modules/has": { - "version": "1.0.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/npm/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/has-unicode": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/hosted-git-info": { - "version": "4.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/http-cache-semantics": { - "version": "4.1.0", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause" - }, - "node_modules/npm/node_modules/http-proxy-agent": { - "version": "4.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/npm/node_modules/http-signature": { - "version": "1.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/npm/node_modules/https-proxy-agent": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/npm/node_modules/humanize-ms": { - "version": "1.2.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/npm/node_modules/iconv-lite": { - "version": "0.6.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm/node_modules/ignore-walk": { - "version": "3.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minimatch": "^3.0.4" - } - }, - "node_modules/npm/node_modules/imurmurhash": { - "version": "0.1.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/npm/node_modules/indent-string": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/infer-owner": { - "version": "1.0.4", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/inflight": { - "version": "1.0.6", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/npm/node_modules/inherits": { - "version": "2.0.4", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/ini": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/init-package-json": { - "version": "2.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.1", - "npm-package-arg": "^8.1.2", - "promzard": "^0.3.0", - "read": "~1.0.1", - "read-package-json": "^4.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/ip": { - "version": "1.1.5", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/ip-regex": { - "version": "4.3.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/is-cidr": { - "version": "4.0.2", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "cidr-regex": "^3.1.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/is-core-module": { - "version": "2.6.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/npm/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/npm/node_modules/is-lambda": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/is-typedarray": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/isstream": { - "version": "0.1.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/jsbn": { - "version": "0.1.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/json-schema": { - "version": "0.2.3", - "dev": true, - "inBundle": true - }, - "node_modules/npm/node_modules/json-schema-traverse": { - "version": "0.4.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/json-stringify-nice": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/json-stringify-safe": { - "version": "5.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/jsonparse": { - "version": "1.3.1", - "dev": true, - "engines": [ - "node >= 0.2.0" - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/jsprim": { - "version": "1.4.1", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "inBundle": true, - "license": "MIT", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "node_modules/npm/node_modules/just-diff": { - "version": "3.1.1", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/just-diff-apply": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/libnpmaccess": { - "version": "4.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "minipass": "^3.1.1", - "npm-package-arg": "^8.1.2", - "npm-registry-fetch": "^11.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/libnpmdiff": { - "version": "2.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/disparity-colors": "^1.0.1", - "@npmcli/installed-package-contents": "^1.0.7", - "binary-extensions": "^2.2.0", - "diff": "^5.0.0", - "minimatch": "^3.0.4", - "npm-package-arg": "^8.1.4", - "pacote": "^11.3.4", - "tar": "^6.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/libnpmexec": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^2.3.0", - "@npmcli/ci-detect": "^1.3.0", - "@npmcli/run-script": "^1.8.4", - "chalk": "^4.1.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-package-arg": "^8.1.2", - "pacote": "^11.3.1", - "proc-log": "^1.0.0", - "read": "^1.0.7", - "read-package-json-fast": "^2.0.2", - "walk-up-path": "^1.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/libnpmfund": { - "version": "1.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/arborist": "^2.5.0" - } - }, - "node_modules/npm/node_modules/libnpmhook": { - "version": "6.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^11.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/libnpmorg": { - "version": "2.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^11.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/libnpmpack": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/run-script": "^1.8.3", - "npm-package-arg": "^8.1.0", - "pacote": "^11.2.6" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/libnpmpublish": { - "version": "4.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "normalize-package-data": "^3.0.2", - "npm-package-arg": "^8.1.2", - "npm-registry-fetch": "^11.0.0", - "semver": "^7.1.3", - "ssri": "^8.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/libnpmsearch": { - "version": "3.1.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^11.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/libnpmteam": { - "version": "2.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^11.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/libnpmversion": { - "version": "1.2.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^2.0.7", - "@npmcli/run-script": "^1.8.4", - "json-parse-even-better-errors": "^2.3.1", - "semver": "^7.3.5", - "stringify-package": "^1.0.1" - } - }, - "node_modules/npm/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/make-fetch-happen": { - "version": "9.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/mime-db": { - "version": "1.49.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/npm/node_modules/mime-types": { - "version": "2.1.32", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.49.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/npm/node_modules/minimatch": { - "version": "3.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/npm/node_modules/minipass": { - "version": "3.1.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-collect": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/minipass-fetch": { - "version": "1.4.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.1.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "optionalDependencies": { - "encoding": "^0.1.12" - } - }, - "node_modules/npm/node_modules/minipass-flush": { - "version": "1.0.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/minipass-json-stream": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" - } - }, - "node_modules/npm/node_modules/minipass-pipeline": { - "version": "1.2.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minipass-sized": { - "version": "1.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/minizlib": { - "version": "2.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/mkdirp": { - "version": "1.0.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/mkdirp-infer-owner": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "infer-owner": "^1.0.4", - "mkdirp": "^1.0.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/ms": { - "version": "2.1.3", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/mute-stream": { - "version": "0.0.8", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/negotiator": { - "version": "0.6.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/npm/node_modules/node-gyp": { - "version": "7.1.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.3", - "nopt": "^5.0.0", - "npmlog": "^4.1.2", - "request": "^2.88.2", - "rimraf": "^3.0.2", - "semver": "^7.3.2", - "tar": "^6.0.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": ">= 10.12.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/aproba": { - "version": "1.2.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/node-gyp/node_modules/gauge": { - "version": "2.7.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/npmlog": { - "version": "4.1.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "node_modules/npm/node_modules/node-gyp/node_modules/string-width": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm/node_modules/nopt": { - "version": "5.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/npm/node_modules/normalize-package-data": { - "version": "3.0.3", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/npm-audit-report": { - "version": "2.1.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "chalk": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/npm-bundled": { - "version": "1.1.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "node_modules/npm/node_modules/npm-install-checks": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "semver": "^7.1.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/npm-package-arg": { - "version": "8.1.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "hosted-git-info": "^4.0.1", - "semver": "^7.3.4", - "validate-npm-package-name": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/npm-packlist": { - "version": "2.2.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.6", - "ignore-walk": "^3.0.3", - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "npm-packlist": "bin/index.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/npm-pick-manifest": { - "version": "6.1.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-install-checks": "^4.0.0", - "npm-normalize-package-bin": "^1.0.1", - "npm-package-arg": "^8.1.2", - "semver": "^7.3.4" - } - }, - "node_modules/npm/node_modules/npm-profile": { - "version": "5.0.4", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-registry-fetch": "^11.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "11.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "make-fetch-happen": "^9.0.1", - "minipass": "^3.1.3", - "minipass-fetch": "^1.3.0", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.0.0", - "npm-package-arg": "^8.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/npm-user-validate": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause" - }, - "node_modules/npm/node_modules/npmlog": { - "version": "5.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "node_modules/npm/node_modules/npmlog/node_modules/are-we-there-yet": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/number-is-nan": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm/node_modules/oauth-sign": { - "version": "0.9.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/npm/node_modules/object-assign": { - "version": "4.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm/node_modules/once": { - "version": "1.4.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/npm/node_modules/opener": { - "version": "1.5.2", - "dev": true, - "inBundle": true, - "license": "(WTFPL OR MIT)", - "bin": { - "opener": "bin/opener-bin.js" - } - }, - "node_modules/npm/node_modules/p-map": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm/node_modules/pacote": { - "version": "11.3.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "@npmcli/git": "^2.1.0", - "@npmcli/installed-package-contents": "^1.0.6", - "@npmcli/promise-spawn": "^1.2.0", - "@npmcli/run-script": "^1.8.2", - "cacache": "^15.0.5", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.3", - "mkdirp": "^1.0.3", - "npm-package-arg": "^8.0.1", - "npm-packlist": "^2.1.4", - "npm-pick-manifest": "^6.0.0", - "npm-registry-fetch": "^11.0.0", - "promise-retry": "^2.0.1", - "read-package-json-fast": "^2.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.1.0" - }, - "bin": { - "pacote": "lib/bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/parse-conflict-json": { - "version": "1.1.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^2.3.0", - "just-diff": "^3.0.1", - "just-diff-apply": "^3.0.0" - } - }, - "node_modules/npm/node_modules/path-is-absolute": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm/node_modules/performance-now": { - "version": "2.1.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/proc-log": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/promise-all-reject-late": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/promise-call-limit": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/promise-inflight": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/promise-retry": { - "version": "2.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/promzard": { - "version": "0.3.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "read": "1" - } - }, - "node_modules/npm/node_modules/psl": { - "version": "1.8.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/punycode": { - "version": "2.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/npm/node_modules/qrcode-terminal": { - "version": "0.12.0", - "dev": true, - "inBundle": true, - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" - } - }, - "node_modules/npm/node_modules/qs": { - "version": "6.5.2", - "dev": true, - "inBundle": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/npm/node_modules/read": { - "version": "1.0.7", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "mute-stream": "~0.0.4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/npm/node_modules/read-cmd-shim": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/read-package-json": { - "version": "4.1.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.1", - "json-parse-even-better-errors": "^2.3.0", - "normalize-package-data": "^3.0.0", - "npm-normalize-package-bin": "^1.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/read-package-json-fast": { - "version": "2.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/readable-stream": { - "version": "3.6.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/npm/node_modules/readdir-scoped-modules": { - "version": "1.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" - } - }, - "node_modules/npm/node_modules/request": { - "version": "2.88.2", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/npm/node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/npm/node_modules/request/node_modules/tough-cookie": { - "version": "2.5.0", - "dev": true, - "inBundle": true, - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/npm/node_modules/retry": { - "version": "0.12.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/npm/node_modules/rimraf": { - "version": "3.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/npm/node_modules/safe-buffer": { - "version": "5.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/safer-buffer": { - "version": "2.1.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/semver": { - "version": "7.3.5", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/set-blocking": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/signal-exit": { - "version": "3.0.3", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/smart-buffer": { - "version": "4.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/npm/node_modules/socks": { - "version": "2.6.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ip": "^1.1.5", - "smart-buffer": "^4.1.0" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/npm/node_modules/socks-proxy-agent": { - "version": "6.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.1", - "socks": "^2.6.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/spdx-correct": { - "version": "3.1.1", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-exceptions": { - "version": "2.3.0", - "dev": true, - "inBundle": true, - "license": "CC-BY-3.0" - }, - "node_modules/npm/node_modules/spdx-expression-parse": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.10", - "dev": true, - "inBundle": true, - "license": "CC0-1.0" - }, - "node_modules/npm/node_modules/sshpk": { - "version": "1.16.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm/node_modules/ssri": { - "version": "8.0.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/string_decoder": { - "version": "1.3.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/npm/node_modules/string-width": { - "version": "2.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm/node_modules/string-width/node_modules/ansi-regex": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/npm/node_modules/string-width/node_modules/strip-ansi": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm/node_modules/stringify-package": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/strip-ansi": { - "version": "3.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm/node_modules/tar": { - "version": "6.1.11", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/text-table": { - "version": "0.2.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/tiny-relative-date": { - "version": "1.3.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/treeverse": { - "version": "1.0.4", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/tunnel-agent": { - "version": "0.6.0", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/npm/node_modules/tweetnacl": { - "version": "0.14.5", - "dev": true, - "inBundle": true, - "license": "Unlicense" - }, - "node_modules/npm/node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/npm/node_modules/unique-filename": { - "version": "1.1.1", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "unique-slug": "^2.0.0" - } - }, - "node_modules/npm/node_modules/unique-slug": { - "version": "2.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" - } - }, - "node_modules/npm/node_modules/uri-js": { - "version": "4.4.1", - "dev": true, - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/npm/node_modules/util-deprecate": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/uuid": { - "version": "3.4.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/npm/node_modules/validate-npm-package-license": { - "version": "3.0.4", - "dev": true, - "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/npm/node_modules/validate-npm-package-name": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "builtins": "^1.0.3" - } - }, - "node_modules/npm/node_modules/verror": { - "version": "1.10.0", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "inBundle": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/npm/node_modules/walk-up-path": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/wcwidth": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/npm/node_modules/which": { - "version": "2.0.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/npm/node_modules/wide-align": { - "version": "1.1.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/npm/node_modules/wrappy": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/write-file-atomic": { - "version": "3.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/npm/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/object-inspect": { - "version": "1.11.0", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/p-each-series": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-event": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-timeout": "^3.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-filter": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-map": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-is-promise": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-locate/node_modules/p-try": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-map": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/p-reduce": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/p-retry": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/retry": "^0.12.0", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-timeout": { - "version": "3.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-finally": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-path": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "is-ssh": "^1.3.0", - "protocols": "^1.4.0", - "qs": "^6.9.4", - "query-string": "^6.13.8" - } - }, - "node_modules/path-exists": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/path-type": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picomatch": { - "version": "2.2.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-conf": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^2.0.0", - "load-json-file": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/protocols": { - "version": "1.4.8", - "dev": true, - "license": "MIT" - }, - "node_modules/pump": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/q": { - "version": "1.5.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/qs": { - "version": "6.10.1", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/query-string": { - "version": "6.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/quick-lru": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "dev": true, - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/read-pkg": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/path-exists": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } - }, - "node_modules/readable-stream": { - "version": "2.3.7", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/redent": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/redeyed": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "esprima": "~4.0.0" - } - }, - "node_modules/registry-auth-token": { - "version": "4.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "rc": "^1.2.8" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.19.0", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-global": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "global-dirs": "^0.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/responselike": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "lowercase-keys": "^2.0.0" - } - }, - "node_modules/retry": { - "version": "0.12.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.1.10", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.2.4", - "dev": true, - "license": "ISC" - }, - "node_modules/semantic-release": { - "version": "17.4.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@semantic-release/commit-analyzer": "^8.0.0", - "@semantic-release/error": "^2.2.0", - "@semantic-release/github": "^7.0.0", - "@semantic-release/npm": "^7.0.0", - "@semantic-release/release-notes-generator": "^9.0.0", - "aggregate-error": "^3.0.0", - "cosmiconfig": "^7.0.0", - "debug": "^4.0.0", - "env-ci": "^5.0.0", - "execa": "^5.0.0", - "figures": "^3.0.0", - "find-versions": "^4.0.0", - "get-stream": "^6.0.0", - "git-log-parser": "^1.2.0", - "hook-std": "^2.0.0", - "hosted-git-info": "^4.0.0", - "lodash": "^4.17.21", - "marked": "^2.0.0", - "marked-terminal": "^4.1.1", - "micromatch": "^4.0.2", - "p-each-series": "^2.1.0", - "p-reduce": "^2.0.0", - "read-pkg-up": "^7.0.0", - "resolve-from": "^5.0.0", - "semver": "^7.3.2", - "semver-diff": "^3.1.1", - "signale": "^1.2.1", - "yargs": "^16.2.0" - }, - "bin": { - "semantic-release": "bin/semantic-release.js" - }, - "engines": { - "node": ">=10.19" - } - }, - "node_modules/semantic-release-dotnet": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "glob": "^7.1.7", - "glob-promise": "^4.2.0", - "xml-js": "^1.6.11" - } - }, - "node_modules/semantic-release-nuget": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/semantic-release-nuget/-/semantic-release-nuget-1.1.0.tgz", - "integrity": "sha512-Eaqb39E+dPTgYgHif0/P6cUj7IxZxI9e2BgmfE+NkRlPArpRPa94UZTi8UFMbEKiKg0IaNnAO79yexUPcZnuHA==", - "dev": true - }, - "node_modules/semantic-release/node_modules/get-stream": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/semantic-release/node_modules/hosted-git-info": { - "version": "4.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semantic-release/node_modules/semver": { - "version": "7.3.4", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semantic-release/node_modules/yargs": { - "version": "16.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "5.7.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/semver-diff": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/semver-diff/node_modules/semver": { - "version": "6.3.0", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/semver-regex": { - "version": "3.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.3", - "dev": true, - "license": "ISC" - }, - "node_modules/signale": { - "version": "1.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^2.3.2", - "figures": "^2.0.0", - "pkg-conf": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/signale/node_modules/figures": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/spawn-error-forwarder": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "dev": true, - "license": "CC-BY-3.0" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.7", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/split": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "through": "2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/split-on-first": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/split2": { - "version": "1.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "through2": "~2.0.0" - } - }, - "node_modules/stream-combiner2": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" - } - }, - "node_modules/strict-uri-encode": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width": { - "version": "4.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/temp-dir": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/tempy": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "del": "^6.0.0", - "is-stream": "^2.0.0", - "temp-dir": "^2.0.0", - "type-fest": "^0.16.0", - "unique-string": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/type-fest": { - "version": "0.16.0", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/text-extensions": { - "version": "1.9.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/through": { - "version": "2.3.8", - "dev": true, - "license": "MIT" - }, - "node_modules/through2": { - "version": "2.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/to-readable-stream": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/traverse": { - "version": "0.6.6", - "dev": true, - "license": "MIT" - }, - "node_modules/trim-newlines": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/trim-off-newlines": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/type-fest": { - "version": "0.6.0", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } - }, - "node_modules/uglify-js": { - "version": "3.12.5", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/unique-string": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/universal-user-agent": { - "version": "6.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/universalify": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/url-join": { - "version": "4.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "dev": true, - "license": "ISC" - }, - "node_modules/xml-js": { - "version": "1.6.11", - "dev": true, - "license": "MIT", - "dependencies": { - "sax": "^1.2.4" - }, - "bin": { - "xml-js": "bin/cli.js" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/yaml": { - "version": "1.10.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "17.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.4", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "dev": true - }, - "@babel/highlight": { - "version": "7.10.4", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@commitlint/cli": { - "version": "13.1.0", - "dev": true, - "requires": { - "@commitlint/format": "^13.1.0", - "@commitlint/lint": "^13.1.0", - "@commitlint/load": "^13.1.0", - "@commitlint/read": "^13.1.0", - "@commitlint/types": "^13.1.0", - "lodash": "^4.17.19", - "resolve-from": "5.0.0", - "resolve-global": "1.0.0", - "yargs": "^17.0.0" - } - }, - "@commitlint/config-conventional": { - "version": "13.1.0", - "dev": true, - "requires": { - "conventional-changelog-conventionalcommits": "^4.3.1" - } - }, - "@commitlint/ensure": { - "version": "13.1.0", - "dev": true, - "requires": { - "@commitlint/types": "^13.1.0", - "lodash": "^4.17.19" - } - }, - "@commitlint/execute-rule": { - "version": "13.0.0", - "dev": true - }, - "@commitlint/format": { - "version": "13.1.0", - "dev": true, - "requires": { - "@commitlint/types": "^13.1.0", - "chalk": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } - } - }, - "@commitlint/is-ignored": { - "version": "13.1.0", - "dev": true, - "requires": { - "@commitlint/types": "^13.1.0", - "semver": "7.3.5" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@commitlint/lint": { - "version": "13.1.0", - "dev": true, - "requires": { - "@commitlint/is-ignored": "^13.1.0", - "@commitlint/parse": "^13.1.0", - "@commitlint/rules": "^13.1.0", - "@commitlint/types": "^13.1.0" - } - }, - "@commitlint/load": { - "version": "13.1.0", - "dev": true, - "requires": { - "@commitlint/execute-rule": "^13.0.0", - "@commitlint/resolve-extends": "^13.0.0", - "@commitlint/types": "^13.1.0", - "chalk": "^4.0.0", - "cosmiconfig": "^7.0.0", - "lodash": "^4.17.19", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } - } - }, - "@commitlint/message": { - "version": "13.0.0", - "dev": true - }, - "@commitlint/parse": { - "version": "13.1.0", - "dev": true, - "requires": { - "@commitlint/types": "^13.1.0", - "conventional-changelog-angular": "^5.0.11", - "conventional-commits-parser": "^3.0.0" - } - }, - "@commitlint/read": { - "version": "13.1.0", - "dev": true, - "requires": { - "@commitlint/top-level": "^13.0.0", - "@commitlint/types": "^13.1.0", - "fs-extra": "^10.0.0", - "git-raw-commits": "^2.0.0" - }, - "dependencies": { - "fs-extra": { - "version": "10.0.0", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - } - } - }, - "@commitlint/resolve-extends": { - "version": "13.0.0", - "dev": true, - "requires": { - "import-fresh": "^3.0.0", - "lodash": "^4.17.19", - "resolve-from": "^5.0.0", - "resolve-global": "^1.0.0" - } - }, - "@commitlint/rules": { - "version": "13.1.0", - "dev": true, - "requires": { - "@commitlint/ensure": "^13.1.0", - "@commitlint/message": "^13.0.0", - "@commitlint/to-lines": "^13.0.0", - "@commitlint/types": "^13.1.0", - "execa": "^5.0.0" - } - }, - "@commitlint/to-lines": { - "version": "13.0.0", - "dev": true - }, - "@commitlint/top-level": { - "version": "13.0.0", - "dev": true, - "requires": { - "find-up": "^5.0.0" - }, - "dependencies": { - "find-up": { - "version": "5.0.0", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "6.0.0", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "p-limit": { - "version": "3.1.0", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "path-exists": { - "version": "4.0.0", - "dev": true - } - } - }, - "@commitlint/types": { - "version": "13.1.0", - "dev": true, - "requires": { - "chalk": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.4", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.4", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.4", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.6", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.4", - "fastq": "^1.6.0" - } - }, - "@octokit/auth-token": { - "version": "2.4.4", - "dev": true, - "requires": { - "@octokit/types": "^6.0.0" - } - }, - "@octokit/core": { - "version": "3.2.4", - "dev": true, - "requires": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.4.12", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.1.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/endpoint": { - "version": "6.0.10", - "dev": true, - "requires": { - "@octokit/types": "^6.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/graphql": { - "version": "4.5.8", - "dev": true, - "requires": { - "@octokit/request": "^5.3.0", - "@octokit/types": "^6.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/openapi-types": { - "version": "3.1.1", - "dev": true - }, - "@octokit/plugin-paginate-rest": { - "version": "2.8.0", - "dev": true, - "requires": { - "@octokit/types": "^6.4.0" - } - }, - "@octokit/plugin-request-log": { - "version": "1.0.2", - "dev": true, - "requires": {} - }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "4.4.1", - "dev": true, - "requires": { - "@octokit/types": "^6.1.0", - "deprecation": "^2.3.1" - } - }, - "@octokit/request": { - "version": "5.4.12", - "dev": true, - "requires": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.0.0", - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.1", - "once": "^1.4.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/request-error": { - "version": "2.0.4", - "dev": true, - "requires": { - "@octokit/types": "^6.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/rest": { - "version": "18.0.12", - "dev": true, - "requires": { - "@octokit/core": "^3.2.3", - "@octokit/plugin-paginate-rest": "^2.6.2", - "@octokit/plugin-request-log": "^1.0.2", - "@octokit/plugin-rest-endpoint-methods": "4.4.1" - } - }, - "@octokit/types": { - "version": "6.4.2", - "dev": true, - "requires": { - "@octokit/openapi-types": "^3.1.1", - "@types/node": ">= 8" - } - }, - "@semantic-release/changelog": { - "version": "5.0.1", - "dev": true, - "requires": { - "@semantic-release/error": "^2.1.0", - "aggregate-error": "^3.0.0", - "fs-extra": "^9.0.0", - "lodash": "^4.17.4" - } - }, - "@semantic-release/commit-analyzer": { - "version": "8.0.1", - "dev": true, - "requires": { - "conventional-changelog-angular": "^5.0.0", - "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.0.7", - "debug": "^4.0.0", - "import-from": "^3.0.0", - "lodash": "^4.17.4", - "micromatch": "^4.0.2" - } - }, - "@semantic-release/error": { - "version": "2.2.0", - "dev": true - }, - "@semantic-release/git": { - "version": "9.0.0", - "dev": true, - "requires": { - "@semantic-release/error": "^2.1.0", - "aggregate-error": "^3.0.0", - "debug": "^4.0.0", - "dir-glob": "^3.0.0", - "execa": "^4.0.0", - "lodash": "^4.17.4", - "micromatch": "^4.0.0", - "p-reduce": "^2.0.0" - }, - "dependencies": { - "execa": { - "version": "4.1.0", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - } - } - }, - "@semantic-release/github": { - "version": "7.2.0", - "dev": true, - "requires": { - "@octokit/rest": "^18.0.0", - "@semantic-release/error": "^2.2.0", - "aggregate-error": "^3.0.0", - "bottleneck": "^2.18.1", - "debug": "^4.0.0", - "dir-glob": "^3.0.0", - "fs-extra": "^9.0.0", - "globby": "^11.0.0", - "http-proxy-agent": "^4.0.0", - "https-proxy-agent": "^5.0.0", - "issue-parser": "^6.0.0", - "lodash": "^4.17.4", - "mime": "^2.4.3", - "p-filter": "^2.0.0", - "p-retry": "^4.0.0", - "url-join": "^4.0.0" - } - }, - "@semantic-release/gitlab": { - "version": "6.2.2", - "dev": true, - "requires": { - "@semantic-release/error": "^2.2.0", - "aggregate-error": "^3.0.0", - "debug": "^4.0.0", - "dir-glob": "^3.0.0", - "escape-string-regexp": "^3.0.0", - "form-data": "^3.0.0", - "fs-extra": "^9.0.0", - "globby": "^11.0.0", - "got": "^10.5.2", - "lodash": "^4.17.11", - "parse-path": "^4.0.0", - "url-join": "^4.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "3.0.0", - "dev": true - } - } - }, - "@semantic-release/npm": { - "version": "7.1.3", - "dev": true, - "requires": { - "@semantic-release/error": "^2.2.0", - "aggregate-error": "^3.0.0", - "execa": "^5.0.0", - "fs-extra": "^10.0.0", - "lodash": "^4.17.15", - "nerf-dart": "^1.0.0", - "normalize-url": "^6.0.0", - "npm": "^7.0.0", - "rc": "^1.2.8", - "read-pkg": "^5.0.0", - "registry-auth-token": "^4.0.0", - "semver": "^7.1.2", - "tempy": "^1.0.0" - }, - "dependencies": { - "fs-extra": { - "version": "10.0.0", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "semver": { - "version": "7.3.4", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@semantic-release/release-notes-generator": { - "version": "9.0.1", - "dev": true, - "requires": { - "conventional-changelog-angular": "^5.0.0", - "conventional-changelog-writer": "^4.0.0", - "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.0.0", - "debug": "^4.0.0", - "get-stream": "^5.0.0", - "import-from": "^3.0.0", - "into-stream": "^5.0.0", - "lodash": "^4.17.4", - "read-pkg-up": "^7.0.0" - } - }, - "@sindresorhus/is": { - "version": "2.1.1", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "4.0.6", - "dev": true, - "requires": { - "defer-to-connect": "^2.0.0" - } - }, - "@tootallnate/once": { - "version": "1.1.2", - "dev": true - }, - "@types/cacheable-request": { - "version": "6.0.2", - "dev": true, - "requires": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" - } - }, - "@types/glob": { - "version": "7.1.4", - "dev": true, - "requires": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "@types/http-cache-semantics": { - "version": "4.0.1", - "dev": true - }, - "@types/keyv": { - "version": "3.1.2", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/minimatch": { - "version": "3.0.5", - "dev": true - }, - "@types/minimist": { - "version": "1.2.1", - "dev": true - }, - "@types/node": { - "version": "14.14.22", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.0", - "dev": true - }, - "@types/parse-json": { - "version": "4.0.0", - "dev": true - }, - "@types/responselike": { - "version": "1.0.0", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/retry": { - "version": "0.12.0", - "dev": true - }, - "aggregate-error": { - "version": "3.1.0", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ansi-escapes": { - "version": "4.3.2", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.0", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "ansicolors": { - "version": "0.3.2", - "dev": true - }, - "argv-formatter": { - "version": "1.0.0", - "dev": true - }, - "array-ify": { - "version": "1.0.0", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "dev": true - }, - "at-least-node": { - "version": "1.0.0", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "dev": true - }, - "before-after-hook": { - "version": "2.1.0", - "dev": true - }, - "bottleneck": { - "version": "2.19.5", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "cacheable-lookup": { - "version": "2.0.1", - "dev": true, - "requires": { - "@types/keyv": "^3.1.1", - "keyv": "^4.0.0" - } - }, - "cacheable-request": { - "version": "7.0.2", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" - } - }, - "call-bind": { - "version": "1.0.2", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "dev": true - }, - "camelcase-keys": { - "version": "6.2.2", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "dev": true - }, - "map-obj": { - "version": "4.1.0", - "dev": true - } - } - }, - "cardinal": { - "version": "2.1.1", - "dev": true, - "requires": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" - } - }, - "chalk": { - "version": "2.4.2", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "color-convert": { - "version": "1.9.3", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "clean-stack": { - "version": "2.2.0", - "dev": true - }, - "cli-table": { - "version": "0.3.6", - "dev": true, - "requires": { - "colors": "1.0.3" - } - }, - "cliui": { - "version": "7.0.4", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "clone-response": { - "version": "1.0.2", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - }, - "dependencies": { - "mimic-response": { - "version": "1.0.1", - "dev": true - } - } - }, - "color-convert": { - "version": "2.0.1", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "dev": true - }, - "colors": { - "version": "1.0.3", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commitlint-gitlab-ci": { - "version": "0.0.4", - "dev": true, - "requires": { - "@commitlint/cli": "^13.1.0" - } - }, - "compare-func": { - "version": "2.0.0", - "dev": true, - "requires": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - } - }, - "concat-map": { - "version": "0.0.1", - "dev": true - }, - "conventional-changelog-angular": { - "version": "5.0.12", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "q": "^1.5.1" - } - }, - "conventional-changelog-conventionalcommits": { - "version": "4.5.0", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "lodash": "^4.17.15", - "q": "^1.5.1" - } - }, - "conventional-changelog-writer": { - "version": "4.1.0", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "conventional-commits-filter": "^2.0.7", - "dateformat": "^3.0.0", - "handlebars": "^4.7.6", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "semver": "^6.0.0", - "split": "^1.0.0", - "through2": "^4.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "semver": { - "version": "6.3.0", - "dev": true - }, - "string_decoder": { - "version": "1.3.0", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "through2": { - "version": "4.0.2", - "dev": true, - "requires": { - "readable-stream": "3" - } - } - } - }, - "conventional-commits-filter": { - "version": "2.0.7", - "dev": true, - "requires": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.0" - } - }, - "conventional-commits-parser": { - "version": "3.2.0", - "dev": true, - "requires": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.0.4", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^2.0.0", - "through2": "^4.0.0", - "trim-off-newlines": "^1.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "dev": true - }, - "split2": { - "version": "2.2.0", - "dev": true, - "requires": { - "through2": "^2.0.2" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "through2": { - "version": "2.0.5", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, - "string_decoder": { - "version": "1.3.0", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "dev": true - } - } - }, - "through2": { - "version": "4.0.2", - "dev": true, - "requires": { - "readable-stream": "3" - } - } - } - }, - "core-util-is": { - "version": "1.0.2", - "dev": true - }, - "cosmiconfig": { - "version": "7.0.0", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "cross-spawn": { - "version": "7.0.3", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "dependencies": { - "which": { - "version": "2.0.2", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "crypto-random-string": { - "version": "2.0.0", - "dev": true - }, - "dargs": { - "version": "7.0.0", - "dev": true - }, - "dateformat": { - "version": "3.0.3", - "dev": true - }, - "debug": { - "version": "4.3.1", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "dev": true - } - } - }, - "decamelize": { - "version": "1.2.0", - "dev": true - }, - "decamelize-keys": { - "version": "1.1.0", - "dev": true, - "requires": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - } - }, - "decode-uri-component": { - "version": "0.2.0", - "dev": true - }, - "decompress-response": { - "version": "5.0.0", - "dev": true, - "requires": { - "mimic-response": "^2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "dev": true - }, - "defer-to-connect": { - "version": "2.0.1", - "dev": true - }, - "del": { - "version": "6.0.0", - "dev": true, - "requires": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - }, - "dependencies": { - "p-map": { - "version": "4.0.0", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "rimraf": { - "version": "3.0.2", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "dev": true - }, - "deprecation": { - "version": "2.3.1", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "dot-prop": { - "version": "5.3.0", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, - "duplexer2": { - "version": "0.1.4", - "dev": true, - "requires": { - "readable-stream": "^2.0.2" - } - }, - "duplexer3": { - "version": "0.1.4", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "env-ci": { - "version": "5.0.2", - "dev": true, - "requires": { - "execa": "^4.0.0", - "java-properties": "^1.0.0" - }, - "dependencies": { - "execa": { - "version": "4.1.0", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - } - } - }, - "error-ex": { - "version": "1.3.2", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "escalade": { - "version": "3.1.1", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "dev": true - }, - "esprima": { - "version": "4.0.1", - "dev": true - }, - "execa": { - "version": "5.1.1", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "dependencies": { - "get-stream": { - "version": "6.0.1", - "dev": true - }, - "human-signals": { - "version": "2.1.0", - "dev": true - } - } - }, - "fast-glob": { - "version": "3.2.5", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - } - }, - "fastq": { - "version": "1.10.0", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "figures": { - "version": "3.2.0", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "fill-range": { - "version": "7.0.1", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "filter-obj": { - "version": "1.1.0", - "dev": true - }, - "find-up": { - "version": "2.1.0", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "find-versions": { - "version": "4.0.0", - "dev": true, - "requires": { - "semver-regex": "^3.1.2" - } - }, - "form-data": { - "version": "3.0.1", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "from2": { - "version": "2.3.0", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "fs-extra": { - "version": "9.1.0", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.1", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "get-stream": { - "version": "5.2.0", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "git-log-parser": { - "version": "1.2.0", - "dev": true, - "requires": { - "argv-formatter": "~1.0.0", - "spawn-error-forwarder": "~1.0.0", - "split2": "~1.0.0", - "stream-combiner2": "~1.1.1", - "through2": "~2.0.0", - "traverse": "~0.6.6" - } - }, - "git-raw-commits": { - "version": "2.0.9", - "dev": true, - "requires": { - "dargs": "^7.0.0", - "lodash.template": "^4.0.2", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "split2": { - "version": "3.2.2", - "dev": true, - "requires": { - "readable-stream": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.3.0", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "through2": { - "version": "4.0.2", - "dev": true, - "requires": { - "readable-stream": "3" - } - } - } - }, - "glob": { - "version": "7.1.7", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.1", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "glob-promise": { - "version": "4.2.0", - "dev": true, - "requires": { - "@types/glob": "^7.1.3" - } - }, - "global-dirs": { - "version": "0.1.1", - "dev": true, - "requires": { - "ini": "^1.3.4" - } - }, - "globby": { - "version": "11.0.2", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, - "got": { - "version": "10.7.0", - "dev": true, - "requires": { - "@sindresorhus/is": "^2.0.0", - "@szmarczak/http-timer": "^4.0.0", - "@types/cacheable-request": "^6.0.1", - "cacheable-lookup": "^2.0.0", - "cacheable-request": "^7.0.1", - "decompress-response": "^5.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^5.0.0", - "lowercase-keys": "^2.0.0", - "mimic-response": "^2.1.0", - "p-cancelable": "^2.0.0", - "p-event": "^4.0.0", - "responselike": "^2.0.0", - "to-readable-stream": "^2.0.0", - "type-fest": "^0.10.0" - }, - "dependencies": { - "type-fest": { - "version": "0.10.0", - "dev": true - } - } - }, - "graceful-fs": { - "version": "4.2.4", - "dev": true - }, - "handlebars": { - "version": "4.7.6", - "dev": true, - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - } - }, - "hard-rejection": { - "version": "2.1.0", - "dev": true - }, - "has": { - "version": "1.0.3", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "has-symbols": { - "version": "1.0.2", - "dev": true - }, - "hook-std": { - "version": "2.0.0", - "dev": true - }, - "hosted-git-info": { - "version": "2.8.8", - "dev": true - }, - "http-cache-semantics": { - "version": "4.1.0", - "dev": true - }, - "http-proxy-agent": { - "version": "4.0.1", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "dependencies": { - "agent-base": { - "version": "6.0.2", - "dev": true, - "requires": { - "debug": "4" - } - } - } - }, - "https-proxy-agent": { - "version": "5.0.0", - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - }, - "dependencies": { - "agent-base": { - "version": "6.0.2", - "dev": true, - "requires": { - "debug": "4" - } - } - } - }, - "human-signals": { - "version": "1.1.1", - "dev": true - }, - "husky": { - "version": "7.0.2", - "dev": true - }, - "ignore": { - "version": "5.1.8", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "dev": true - } - } - }, - "import-from": { - "version": "3.0.0", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "indent-string": { - "version": "4.0.0", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "dev": true - }, - "ini": { - "version": "1.3.8", - "dev": true - }, - "into-stream": { - "version": "5.1.1", - "dev": true, - "requires": { - "from2": "^2.3.0", - "p-is-promise": "^3.0.0" - } - }, - "is-arrayish": { - "version": "0.2.1", - "dev": true - }, - "is-core-module": { - "version": "2.2.0", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-extglob": { - "version": "2.1.1", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "dev": true - }, - "is-obj": { - "version": "2.0.0", - "dev": true - }, - "is-path-cwd": { - "version": "2.2.0", - "dev": true - }, - "is-path-inside": { - "version": "3.0.2", - "dev": true - }, - "is-plain-obj": { - "version": "1.1.0", - "dev": true - }, - "is-plain-object": { - "version": "5.0.0", - "dev": true - }, - "is-ssh": { - "version": "1.3.3", - "dev": true, - "requires": { - "protocols": "^1.1.0" - } - }, - "is-stream": { - "version": "2.0.0", - "dev": true - }, - "is-text-path": { - "version": "1.0.1", - "dev": true, - "requires": { - "text-extensions": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "dev": true - }, - "issue-parser": { - "version": "6.0.0", - "dev": true, - "requires": { - "lodash.capitalize": "^4.2.1", - "lodash.escaperegexp": "^4.1.2", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.uniqby": "^4.7.0" - } - }, - "java-properties": { - "version": "1.0.2", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "dev": true - }, - "json-buffer": { - "version": "3.0.1", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "dev": true - }, - "jsonfile": { - "version": "6.1.0", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "jsonparse": { - "version": "1.3.1", - "dev": true - }, - "JSONStream": { - "version": "1.3.5", - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "keyv": { - "version": "4.0.3", - "dev": true, - "requires": { - "json-buffer": "3.0.1" - } - }, - "kind-of": { - "version": "6.0.3", - "dev": true - }, - "lines-and-columns": { - "version": "1.1.6", - "dev": true - }, - "load-json-file": { - "version": "4.0.0", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "parse-json": { - "version": "4.0.0", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "pify": { - "version": "3.0.0", - "dev": true - } - } - }, - "locate-path": { - "version": "2.0.0", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "dev": true - }, - "lodash._reinterpolate": { - "version": "3.0.0", - "dev": true - }, - "lodash.capitalize": { - "version": "4.2.1", - "dev": true - }, - "lodash.escaperegexp": { - "version": "4.1.2", - "dev": true - }, - "lodash.ismatch": { - "version": "4.4.0", - "dev": true - }, - "lodash.isplainobject": { - "version": "4.0.6", - "dev": true - }, - "lodash.isstring": { - "version": "4.0.1", - "dev": true - }, - "lodash.template": { - "version": "4.5.0", - "dev": true, - "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "lodash.templatesettings": { - "version": "4.2.0", - "dev": true, - "requires": { - "lodash._reinterpolate": "^3.0.0" - } - }, - "lodash.uniqby": { - "version": "4.7.0", - "dev": true - }, - "lowercase-keys": { - "version": "2.0.0", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "map-obj": { - "version": "1.0.1", - "dev": true - }, - "marked": { - "version": "2.1.3", - "dev": true - }, - "marked-terminal": { - "version": "4.1.1", - "dev": true, - "requires": { - "ansi-escapes": "^4.3.1", - "cardinal": "^2.1.1", - "chalk": "^4.1.0", - "cli-table": "^0.3.1", - "node-emoji": "^1.10.0", - "supports-hyperlinks": "^2.1.0" - }, - "dependencies": { - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } - } - }, - "meow": { - "version": "8.1.2", - "dev": true, - "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "dependencies": { - "hosted-git-info": { - "version": "3.0.7", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "normalize-package-data": { - "version": "3.0.0", - "dev": true, - "requires": { - "hosted-git-info": "^3.0.6", - "resolve": "^1.17.0", - "semver": "^7.3.2", - "validate-npm-package-license": "^3.0.1" - } - }, - "semver": { - "version": "7.3.4", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "type-fest": { - "version": "0.18.1", - "dev": true - } - } - }, - "merge-stream": { - "version": "2.0.0", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "mime": { - "version": "2.5.0", - "dev": true - }, - "mime-db": { - "version": "1.49.0", - "dev": true - }, - "mime-types": { - "version": "2.1.32", - "dev": true, - "requires": { - "mime-db": "1.49.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "dev": true - }, - "mimic-response": { - "version": "2.1.0", - "dev": true - }, - "min-indent": { - "version": "1.0.1", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "dev": true - }, - "minimist-options": { - "version": "4.1.0", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - } - }, - "modify-values": { - "version": "1.0.1", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "dev": true - }, - "nerf-dart": { - "version": "1.0.0", - "dev": true - }, - "node-emoji": { - "version": "1.11.0", - "dev": true, - "requires": { - "lodash": "^4.17.21" - } - }, - "node-fetch": { - "version": "2.6.1", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-url": { - "version": "6.1.0", - "dev": true - }, - "npm": { - "version": "7.22.0", - "dev": true, - "requires": { - "@npmcli/arborist": "^2.8.3", - "@npmcli/ci-detect": "^1.2.0", - "@npmcli/config": "^2.3.0", - "@npmcli/map-workspaces": "^1.0.4", - "@npmcli/package-json": "^1.0.1", - "@npmcli/run-script": "^1.8.6", - "abbrev": "~1.1.1", - "ansicolors": "~0.3.2", - "ansistyles": "~0.1.3", - "archy": "~1.0.0", - "cacache": "^15.3.0", - "chalk": "^4.1.2", - "chownr": "^2.0.0", - "cli-columns": "^3.1.2", - "cli-table3": "^0.6.0", - "columnify": "~1.5.4", - "fastest-levenshtein": "^1.0.12", - "glob": "^7.1.7", - "graceful-fs": "^4.2.8", - "hosted-git-info": "^4.0.2", - "ini": "^2.0.0", - "init-package-json": "^2.0.4", - "is-cidr": "^4.0.2", - "json-parse-even-better-errors": "^2.3.1", - "libnpmaccess": "^4.0.2", - "libnpmdiff": "^2.0.4", - "libnpmexec": "^2.0.1", - "libnpmfund": "^1.1.0", - "libnpmhook": "^6.0.2", - "libnpmorg": "^2.0.2", - "libnpmpack": "^2.0.1", - "libnpmpublish": "^4.0.1", - "libnpmsearch": "^3.1.1", - "libnpmteam": "^2.0.3", - "libnpmversion": "^1.2.1", - "make-fetch-happen": "^9.1.0", - "minipass": "^3.1.3", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "ms": "^2.1.2", - "node-gyp": "^7.1.2", - "nopt": "^5.0.0", - "npm-audit-report": "^2.1.5", - "npm-package-arg": "^8.1.5", - "npm-pick-manifest": "^6.1.1", - "npm-profile": "^5.0.3", - "npm-registry-fetch": "^11.0.0", - "npm-user-validate": "^1.0.1", - "npmlog": "^5.0.1", - "opener": "^1.5.2", - "pacote": "^11.3.5", - "parse-conflict-json": "^1.1.1", - "qrcode-terminal": "^0.12.0", - "read": "~1.0.7", - "read-package-json": "^4.1.1", - "read-package-json-fast": "^2.0.3", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "ssri": "^8.0.1", - "tar": "^6.1.11", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", - "treeverse": "^1.0.4", - "validate-npm-package-name": "~3.0.0", - "which": "^2.0.2", - "write-file-atomic": "^3.0.3" - }, - "dependencies": { - "@gar/promisify": { - "version": "1.1.2", - "bundled": true, - "dev": true - }, - "@npmcli/arborist": { - "version": "2.8.3", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/map-workspaces": "^1.0.2", - "@npmcli/metavuln-calculator": "^1.1.0", - "@npmcli/move-file": "^1.1.0", - "@npmcli/name-from-folder": "^1.0.1", - "@npmcli/node-gyp": "^1.0.1", - "@npmcli/package-json": "^1.0.1", - "@npmcli/run-script": "^1.8.2", - "bin-links": "^2.2.1", - "cacache": "^15.0.3", - "common-ancestor-path": "^1.0.1", - "json-parse-even-better-errors": "^2.3.1", - "json-stringify-nice": "^1.1.4", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "npm-install-checks": "^4.0.0", - "npm-package-arg": "^8.1.5", - "npm-pick-manifest": "^6.1.0", - "npm-registry-fetch": "^11.0.0", - "pacote": "^11.3.5", - "parse-conflict-json": "^1.1.1", - "proc-log": "^1.0.0", - "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^1.0.1", - "read-package-json-fast": "^2.0.2", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "ssri": "^8.0.1", - "treeverse": "^1.0.4", - "walk-up-path": "^1.0.0" - } - }, - "@npmcli/ci-detect": { - "version": "1.3.0", - "bundled": true, - "dev": true - }, - "@npmcli/config": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "requires": { - "ini": "^2.0.0", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^5.0.0", - "semver": "^7.3.4", - "walk-up-path": "^1.0.0" - } - }, - "@npmcli/disparity-colors": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "^4.3.0" - } - }, - "@npmcli/fs": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "@gar/promisify": "^1.0.1", - "semver": "^7.3.5" - } - }, - "@npmcli/git": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/promise-spawn": "^1.3.2", - "lru-cache": "^6.0.0", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^6.1.1", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^2.0.2" - } - }, - "@npmcli/installed-package-contents": { - "version": "1.0.7", - "bundled": true, - "dev": true, - "requires": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "@npmcli/map-workspaces": { - "version": "1.0.4", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/name-from-folder": "^1.0.1", - "glob": "^7.1.6", - "minimatch": "^3.0.4", - "read-package-json-fast": "^2.0.1" - } - }, - "@npmcli/metavuln-calculator": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "cacache": "^15.0.5", - "pacote": "^11.1.11", - "semver": "^7.3.2" - } - }, - "@npmcli/move-file": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "requires": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - } - }, - "@npmcli/name-from-folder": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "@npmcli/node-gyp": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "@npmcli/package-json": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "json-parse-even-better-errors": "^2.3.1" - } - }, - "@npmcli/promise-spawn": { - "version": "1.3.2", - "bundled": true, - "dev": true, - "requires": { - "infer-owner": "^1.0.4" - } - }, - "@npmcli/run-script": { - "version": "1.8.6", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/node-gyp": "^1.0.2", - "@npmcli/promise-spawn": "^1.3.2", - "node-gyp": "^7.1.0", - "read-package-json-fast": "^2.0.1" - } - }, - "@tootallnate/once": { - "version": "1.1.2", - "bundled": true, - "dev": true - }, - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "bundled": true, - "dev": true, - "requires": { - "debug": "4" - } - }, - "agentkeepalive": { - "version": "4.1.4", - "bundled": true, - "dev": true, - "requires": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" - } - }, - "aggregate-error": { - "version": "3.1.0", - "bundled": true, - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "6.12.6", - "bundled": true, - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "bundled": true, - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "ansicolors": { - "version": "0.3.2", - "bundled": true, - "dev": true - }, - "ansistyles": { - "version": "0.1.3", - "bundled": true, - "dev": true - }, - "aproba": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "archy": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "are-we-there-yet": { - "version": "1.1.6", - "bundled": true, - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - } - }, - "asap": { - "version": "2.0.6", - "bundled": true, - "dev": true - }, - "asn1": { - "version": "0.2.4", - "bundled": true, - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "bundled": true, - "dev": true - }, - "aws-sign2": { - "version": "0.7.0", - "bundled": true, - "dev": true - }, - "aws4": { - "version": "1.11.0", - "bundled": true, - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "bin-links": { - "version": "2.2.1", - "bundled": true, - "dev": true, - "requires": { - "cmd-shim": "^4.0.1", - "mkdirp": "^1.0.3", - "npm-normalize-package-bin": "^1.0.0", - "read-cmd-shim": "^2.0.0", - "rimraf": "^3.0.0", - "write-file-atomic": "^3.0.3" - } - }, - "binary-extensions": { - "version": "2.2.0", - "bundled": true, - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "builtins": { - "version": "1.0.3", - "bundled": true, - "dev": true - }, - "cacache": { - "version": "15.3.0", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - } - }, - "caseless": { - "version": "0.12.0", - "bundled": true, - "dev": true - }, - "chalk": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "chownr": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "cidr-regex": { - "version": "3.1.1", - "bundled": true, - "dev": true, - "requires": { - "ip-regex": "^4.1.0" - } - }, - "clean-stack": { - "version": "2.2.0", - "bundled": true, - "dev": true - }, - "cli-columns": { - "version": "3.1.2", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^2.0.0", - "strip-ansi": "^3.0.1" - } - }, - "cli-table3": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "requires": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", - "string-width": "^4.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "bundled": true, - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "string-width": { - "version": "4.2.2", - "bundled": true, - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "clone": { - "version": "1.0.4", - "bundled": true, - "dev": true - }, - "cmd-shim": { - "version": "4.1.0", - "bundled": true, - "dev": true, - "requires": { - "mkdirp-infer-owner": "^2.0.0" - } - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "bundled": true, - "dev": true - }, - "color-support": { - "version": "1.1.3", - "bundled": true, - "dev": true - }, - "colors": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true - }, - "columnify": { - "version": "1.5.4", - "bundled": true, - "dev": true, - "requires": { - "strip-ansi": "^3.0.0", - "wcwidth": "^1.0.0" - } - }, - "combined-stream": { - "version": "1.0.8", - "bundled": true, - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "common-ancestor-path": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "4.3.2", - "bundled": true, - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "bundled": true, - "dev": true - } - } - }, - "debuglog": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "defaults": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "delayed-stream": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "depd": { - "version": "1.1.2", - "bundled": true, - "dev": true - }, - "dezalgo": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "diff": { - "version": "5.0.0", - "bundled": true, - "dev": true - }, - "ecc-jsbn": { - "version": "0.1.2", - "bundled": true, - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "emoji-regex": { - "version": "8.0.0", - "bundled": true, - "dev": true - }, - "encoding": { - "version": "0.1.13", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "iconv-lite": "^0.6.2" - } - }, - "env-paths": { - "version": "2.2.1", - "bundled": true, - "dev": true - }, - "err-code": { - "version": "2.0.3", - "bundled": true, - "dev": true - }, - "extend": { - "version": "3.0.2", - "bundled": true, - "dev": true - }, - "extsprintf": { - "version": "1.3.0", - "bundled": true, - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "bundled": true, - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "fastest-levenshtein": { - "version": "1.0.12", - "bundled": true, - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true, - "dev": true - }, - "fs-minipass": { - "version": "2.1.0", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "bundled": true, - "dev": true - }, - "gauge": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1 || ^2.0.0", - "strip-ansi": "^3.0.1 || ^4.0.0", - "wide-align": "^1.1.2" - } - }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.7", - "bundled": true, - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.8", - "bundled": true, - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "har-validator": { - "version": "5.1.5", - "bundled": true, - "dev": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true - }, - "hosted-git-info": { - "version": "4.0.2", - "bundled": true, - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "http-cache-semantics": { - "version": "4.1.0", - "bundled": true, - "dev": true - }, - "http-proxy-agent": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "http-signature": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-proxy-agent": { - "version": "5.0.0", - "bundled": true, - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "humanize-ms": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "requires": { - "ms": "^2.0.0" - } - }, - "iconv-lite": { - "version": "0.6.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, - "ignore-walk": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "imurmurhash": { - "version": "0.1.4", - "bundled": true, - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "bundled": true, - "dev": true - }, - "infer-owner": { - "version": "1.0.4", - "bundled": true, - "dev": true - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "bundled": true, - "dev": true - }, - "ini": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "init-package-json": { - "version": "2.0.4", - "bundled": true, - "dev": true, - "requires": { - "glob": "^7.1.1", - "npm-package-arg": "^8.1.2", - "promzard": "^0.3.0", - "read": "~1.0.1", - "read-package-json": "^4.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^3.0.0" - } - }, - "ip": { - "version": "1.1.5", - "bundled": true, - "dev": true - }, - "ip-regex": { - "version": "4.3.0", - "bundled": true, - "dev": true - }, - "is-cidr": { - "version": "4.0.2", - "bundled": true, - "dev": true, - "requires": { - "cidr-regex": "^3.1.1" - } - }, - "is-core-module": { - "version": "2.6.0", - "bundled": true, - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "is-lambda": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "isexe": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true, - "dev": true - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "bundled": true, - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true, - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "bundled": true, - "dev": true - }, - "json-stringify-nice": { - "version": "1.1.4", - "bundled": true, - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true, - "dev": true - }, - "jsonparse": { - "version": "1.3.1", - "bundled": true, - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "just-diff": { - "version": "3.1.1", - "bundled": true, - "dev": true - }, - "just-diff-apply": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "libnpmaccess": { - "version": "4.0.3", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "minipass": "^3.1.1", - "npm-package-arg": "^8.1.2", - "npm-registry-fetch": "^11.0.0" - } - }, - "libnpmdiff": { - "version": "2.0.4", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/disparity-colors": "^1.0.1", - "@npmcli/installed-package-contents": "^1.0.7", - "binary-extensions": "^2.2.0", - "diff": "^5.0.0", - "minimatch": "^3.0.4", - "npm-package-arg": "^8.1.4", - "pacote": "^11.3.4", - "tar": "^6.1.0" - } - }, - "libnpmexec": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/arborist": "^2.3.0", - "@npmcli/ci-detect": "^1.3.0", - "@npmcli/run-script": "^1.8.4", - "chalk": "^4.1.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-package-arg": "^8.1.2", - "pacote": "^11.3.1", - "proc-log": "^1.0.0", - "read": "^1.0.7", - "read-package-json-fast": "^2.0.2", - "walk-up-path": "^1.0.0" - } - }, - "libnpmfund": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/arborist": "^2.5.0" - } - }, - "libnpmhook": { - "version": "6.0.3", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^11.0.0" - } - }, - "libnpmorg": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^11.0.0" - } - }, - "libnpmpack": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/run-script": "^1.8.3", - "npm-package-arg": "^8.1.0", - "pacote": "^11.2.6" - } - }, - "libnpmpublish": { - "version": "4.0.2", - "bundled": true, - "dev": true, - "requires": { - "normalize-package-data": "^3.0.2", - "npm-package-arg": "^8.1.2", - "npm-registry-fetch": "^11.0.0", - "semver": "^7.1.3", - "ssri": "^8.0.1" - } - }, - "libnpmsearch": { - "version": "3.1.2", - "bundled": true, - "dev": true, - "requires": { - "npm-registry-fetch": "^11.0.0" - } - }, - "libnpmteam": { - "version": "2.0.4", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^2.0.0", - "npm-registry-fetch": "^11.0.0" - } - }, - "libnpmversion": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/git": "^2.0.7", - "@npmcli/run-script": "^1.8.4", - "json-parse-even-better-errors": "^2.3.1", - "semver": "^7.3.5", - "stringify-package": "^1.0.1" - } - }, - "lru-cache": { - "version": "6.0.0", - "bundled": true, - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "make-fetch-happen": { - "version": "9.1.0", - "bundled": true, - "dev": true, - "requires": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" - } - }, - "mime-db": { - "version": "1.49.0", - "bundled": true, - "dev": true - }, - "mime-types": { - "version": "2.1.32", - "bundled": true, - "dev": true, - "requires": { - "mime-db": "1.49.0" - } - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minipass": { - "version": "3.1.3", - "bundled": true, - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "minipass-collect": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minipass-fetch": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "requires": { - "encoding": "^0.1.12", - "minipass": "^3.1.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - } - }, - "minipass-flush": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minipass-json-stream": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" - } - }, - "minipass-pipeline": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minipass-sized": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minizlib": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "bundled": true, - "dev": true - }, - "mkdirp-infer-owner": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "chownr": "^2.0.0", - "infer-owner": "^1.0.4", - "mkdirp": "^1.0.3" - } - }, - "ms": { - "version": "2.1.3", - "bundled": true, - "dev": true - }, - "mute-stream": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, - "negotiator": { - "version": "0.6.2", - "bundled": true, - "dev": true - }, - "node-gyp": { - "version": "7.1.2", - "bundled": true, - "dev": true, - "requires": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.3", - "nopt": "^5.0.0", - "npmlog": "^4.1.2", - "request": "^2.88.2", - "rimraf": "^3.0.2", - "semver": "^7.3.2", - "tar": "^6.0.2", - "which": "^2.0.2" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "nopt": { - "version": "5.0.0", - "bundled": true, - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "normalize-package-data": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - } - }, - "npm-audit-report": { - "version": "2.1.5", - "bundled": true, - "dev": true, - "requires": { - "chalk": "^4.0.0" - } - }, - "npm-bundled": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-install-checks": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "semver": "^7.1.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "npm-package-arg": { - "version": "8.1.5", - "bundled": true, - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "semver": "^7.3.4", - "validate-npm-package-name": "^3.0.0" - } - }, - "npm-packlist": { - "version": "2.2.2", - "bundled": true, - "dev": true, - "requires": { - "glob": "^7.1.6", - "ignore-walk": "^3.0.3", - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-pick-manifest": { - "version": "6.1.1", - "bundled": true, - "dev": true, - "requires": { - "npm-install-checks": "^4.0.0", - "npm-normalize-package-bin": "^1.0.1", - "npm-package-arg": "^8.1.2", - "semver": "^7.3.4" - } - }, - "npm-profile": { - "version": "5.0.4", - "bundled": true, - "dev": true, - "requires": { - "npm-registry-fetch": "^11.0.0" - } - }, - "npm-registry-fetch": { - "version": "11.0.0", - "bundled": true, - "dev": true, - "requires": { - "make-fetch-happen": "^9.0.1", - "minipass": "^3.1.3", - "minipass-fetch": "^1.3.0", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.0.0", - "npm-package-arg": "^8.0.0" - } - }, - "npm-user-validate": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "npmlog": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "requires": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - }, - "dependencies": { - "are-we-there-yet": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - } - } - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "bundled": true, - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "opener": { - "version": "1.5.2", - "bundled": true, - "dev": true - }, - "p-map": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "pacote": { - "version": "11.3.5", - "bundled": true, - "dev": true, - "requires": { - "@npmcli/git": "^2.1.0", - "@npmcli/installed-package-contents": "^1.0.6", - "@npmcli/promise-spawn": "^1.2.0", - "@npmcli/run-script": "^1.8.2", - "cacache": "^15.0.5", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.3", - "mkdirp": "^1.0.3", - "npm-package-arg": "^8.0.1", - "npm-packlist": "^2.1.4", - "npm-pick-manifest": "^6.0.0", - "npm-registry-fetch": "^11.0.0", - "promise-retry": "^2.0.1", - "read-package-json-fast": "^2.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.1.0" - } - }, - "parse-conflict-json": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "json-parse-even-better-errors": "^2.3.0", - "just-diff": "^3.0.1", - "just-diff-apply": "^3.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "bundled": true, - "dev": true - }, - "proc-log": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "promise-all-reject-late": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "promise-call-limit": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "promise-inflight": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "promise-retry": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "requires": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - } - }, - "promzard": { - "version": "0.3.0", - "bundled": true, - "dev": true, - "requires": { - "read": "1" - } - }, - "psl": { - "version": "1.8.0", - "bundled": true, - "dev": true - }, - "punycode": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "qrcode-terminal": { - "version": "0.12.0", - "bundled": true, - "dev": true - }, - "qs": { - "version": "6.5.2", - "bundled": true, - "dev": true - }, - "read": { - "version": "1.0.7", - "bundled": true, - "dev": true, - "requires": { - "mute-stream": "~0.0.4" - } - }, - "read-cmd-shim": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "read-package-json": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "requires": { - "glob": "^7.1.1", - "json-parse-even-better-errors": "^2.3.0", - "normalize-package-data": "^3.0.0", - "npm-normalize-package-bin": "^1.0.0" - } - }, - "read-package-json-fast": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "requires": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "readable-stream": { - "version": "3.6.0", - "bundled": true, - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdir-scoped-modules": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" - } - }, - "request": { - "version": "2.88.2", - "bundled": true, - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "form-data": { - "version": "2.3.3", - "bundled": true, - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "tough-cookie": { - "version": "2.5.0", - "bundled": true, - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - } - } - }, - "retry": { - "version": "0.12.0", - "bundled": true, - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.2.1", - "bundled": true, - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true - }, - "semver": { - "version": "7.3.5", - "bundled": true, - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true - }, - "signal-exit": { - "version": "3.0.3", - "bundled": true, - "dev": true - }, - "smart-buffer": { - "version": "4.2.0", - "bundled": true, - "dev": true - }, - "socks": { - "version": "2.6.1", - "bundled": true, - "dev": true, - "requires": { - "ip": "^1.1.5", - "smart-buffer": "^4.1.0" - } - }, - "socks-proxy-agent": { - "version": "6.0.0", - "bundled": true, - "dev": true, - "requires": { - "agent-base": "^6.0.2", - "debug": "^4.3.1", - "socks": "^2.6.1" - } - }, - "spdx-correct": { - "version": "3.1.1", - "bundled": true, - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "bundled": true, - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.10", - "bundled": true, - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "bundled": true, - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "ssri": { - "version": "8.0.1", - "bundled": true, - "dev": true, - "requires": { - "minipass": "^3.1.1" - } - }, - "string_decoder": { - "version": "1.3.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-width": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "bundled": true, - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "stringify-package": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "bundled": true, - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "tar": { - "version": "6.1.11", - "bundled": true, - "dev": true, - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - } - }, - "text-table": { - "version": "0.2.0", - "bundled": true, - "dev": true - }, - "tiny-relative-date": { - "version": "1.3.0", - "bundled": true, - "dev": true - }, - "treeverse": { - "version": "1.0.4", - "bundled": true, - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "dev": true - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "bundled": true, - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "unique-filename": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "uri-js": { - "version": "4.4.1", - "bundled": true, - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "uuid": { - "version": "3.4.0", - "bundled": true, - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "validate-npm-package-name": { - "version": "3.0.0", - "bundled": true, - "dev": true, - "requires": { - "builtins": "^1.0.3" - } - }, - "verror": { - "version": "1.10.0", - "bundled": true, - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "walk-up-path": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "wcwidth": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "which": { - "version": "2.0.2", - "bundled": true, - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "write-file-atomic": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "yallist": { - "version": "4.0.0", - "bundled": true, - "dev": true - } - } - }, - "npm-run-path": { - "version": "4.0.1", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "object-inspect": { - "version": "1.11.0", - "dev": true - }, - "once": { - "version": "1.4.0", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "p-cancelable": { - "version": "2.1.1", - "dev": true - }, - "p-each-series": { - "version": "2.2.0", - "dev": true - }, - "p-event": { - "version": "4.2.0", - "dev": true, - "requires": { - "p-timeout": "^3.1.0" - } - }, - "p-filter": { - "version": "2.1.0", - "dev": true, - "requires": { - "p-map": "^2.0.0" - } - }, - "p-finally": { - "version": "1.0.0", - "dev": true - }, - "p-is-promise": { - "version": "3.0.0", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - }, - "dependencies": { - "p-limit": { - "version": "1.3.0", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-try": { - "version": "1.0.0", - "dev": true - } - } - }, - "p-map": { - "version": "2.1.0", - "dev": true - }, - "p-reduce": { - "version": "2.1.0", - "dev": true - }, - "p-retry": { - "version": "4.2.0", - "dev": true, - "requires": { - "@types/retry": "^0.12.0", - "retry": "^0.12.0" - } - }, - "p-timeout": { - "version": "3.2.0", - "dev": true, - "requires": { - "p-finally": "^1.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parse-path": { - "version": "4.0.3", - "dev": true, - "requires": { - "is-ssh": "^1.3.0", - "protocols": "^1.4.0", - "qs": "^6.9.4", - "query-string": "^6.13.8" - } - }, - "path-exists": { - "version": "3.0.0", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "dev": true - }, - "picomatch": { - "version": "2.2.2", - "dev": true - }, - "pkg-conf": { - "version": "2.1.0", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "load-json-file": "^4.0.0" - } - }, - "process-nextick-args": { - "version": "2.0.1", - "dev": true - }, - "protocols": { - "version": "1.4.8", - "dev": true - }, - "pump": { - "version": "3.0.0", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "q": { - "version": "1.5.1", - "dev": true - }, - "qs": { - "version": "6.10.1", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - }, - "query-string": { - "version": "6.14.1", - "dev": true, - "requires": { - "decode-uri-component": "^0.2.0", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "quick-lru": { - "version": "4.0.1", - "dev": true - }, - "rc": { - "version": "1.2.8", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "read-pkg": { - "version": "5.2.0", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - } - }, - "read-pkg-up": { - "version": "7.0.1", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "dev": true - }, - "type-fest": { - "version": "0.8.1", - "dev": true - } - } - }, - "readable-stream": { - "version": "2.3.7", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "dev": true - } - } - }, - "redent": { - "version": "3.0.0", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } - }, - "redeyed": { - "version": "2.1.1", - "dev": true, - "requires": { - "esprima": "~4.0.0" - } - }, - "registry-auth-token": { - "version": "4.2.1", - "dev": true, - "requires": { - "rc": "^1.2.8" - } - }, - "require-directory": { - "version": "2.1.1", - "dev": true - }, - "resolve": { - "version": "1.19.0", - "dev": true, - "requires": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "5.0.0", - "dev": true - }, - "resolve-global": { - "version": "1.0.0", - "dev": true, - "requires": { - "global-dirs": "^0.1.1" - } - }, - "responselike": { - "version": "2.0.0", - "dev": true, - "requires": { - "lowercase-keys": "^2.0.0" - } - }, - "retry": { - "version": "0.12.0", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "dev": true - }, - "run-parallel": { - "version": "1.1.10", - "dev": true - }, - "safe-buffer": { - "version": "5.2.1", - "dev": true - }, - "sax": { - "version": "1.2.4", - "dev": true - }, - "semantic-release": { - "version": "17.4.7", - "dev": true, - "requires": { - "@semantic-release/commit-analyzer": "^8.0.0", - "@semantic-release/error": "^2.2.0", - "@semantic-release/github": "^7.0.0", - "@semantic-release/npm": "^7.0.0", - "@semantic-release/release-notes-generator": "^9.0.0", - "aggregate-error": "^3.0.0", - "cosmiconfig": "^7.0.0", - "debug": "^4.0.0", - "env-ci": "^5.0.0", - "execa": "^5.0.0", - "figures": "^3.0.0", - "find-versions": "^4.0.0", - "get-stream": "^6.0.0", - "git-log-parser": "^1.2.0", - "hook-std": "^2.0.0", - "hosted-git-info": "^4.0.0", - "lodash": "^4.17.21", - "marked": "^2.0.0", - "marked-terminal": "^4.1.1", - "micromatch": "^4.0.2", - "p-each-series": "^2.1.0", - "p-reduce": "^2.0.0", - "read-pkg-up": "^7.0.0", - "resolve-from": "^5.0.0", - "semver": "^7.3.2", - "semver-diff": "^3.1.1", - "signale": "^1.2.1", - "yargs": "^16.2.0" - }, - "dependencies": { - "get-stream": { - "version": "6.0.1", - "dev": true - }, - "hosted-git-info": { - "version": "4.0.2", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "semver": { - "version": "7.3.4", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yargs": { - "version": "16.2.0", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - } - } - }, - "semantic-release-dotnet": { - "version": "1.0.0", - "dev": true, - "requires": { - "glob": "^7.1.7", - "glob-promise": "^4.2.0", - "xml-js": "^1.6.11" - } - }, - "semantic-release-nuget": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/semantic-release-nuget/-/semantic-release-nuget-1.1.0.tgz", - "integrity": "sha512-Eaqb39E+dPTgYgHif0/P6cUj7IxZxI9e2BgmfE+NkRlPArpRPa94UZTi8UFMbEKiKg0IaNnAO79yexUPcZnuHA==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "dev": true - }, - "semver-diff": { - "version": "3.1.1", - "dev": true, - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "dev": true - } - } - }, - "semver-regex": { - "version": "3.1.2", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.3", - "dev": true - }, - "signale": { - "version": "1.4.0", - "dev": true, - "requires": { - "chalk": "^2.3.2", - "figures": "^2.0.0", - "pkg-conf": "^2.1.0" - }, - "dependencies": { - "figures": { - "version": "2.0.0", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - } - } - }, - "slash": { - "version": "3.0.0", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "dev": true - }, - "spawn-error-forwarder": { - "version": "1.0.0", - "dev": true - }, - "spdx-correct": { - "version": "3.1.1", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.7", - "dev": true - }, - "split": { - "version": "1.0.1", - "dev": true, - "requires": { - "through": "2" - } - }, - "split-on-first": { - "version": "1.1.0", - "dev": true - }, - "split2": { - "version": "1.0.0", - "dev": true, - "requires": { - "through2": "~2.0.0" - } - }, - "stream-combiner2": { - "version": "1.1.1", - "dev": true, - "requires": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" - } - }, - "strict-uri-encode": { - "version": "2.0.0", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "dev": true - } - } - }, - "string-width": { - "version": "4.2.2", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "dev": true - }, - "strip-indent": { - "version": "3.0.0", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-hyperlinks": { - "version": "2.2.0", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - } - }, - "temp-dir": { - "version": "2.0.0", - "dev": true - }, - "tempy": { - "version": "1.0.0", - "dev": true, - "requires": { - "del": "^6.0.0", - "is-stream": "^2.0.0", - "temp-dir": "^2.0.0", - "type-fest": "^0.16.0", - "unique-string": "^2.0.0" - }, - "dependencies": { - "type-fest": { - "version": "0.16.0", - "dev": true - } - } - }, - "text-extensions": { - "version": "1.9.0", - "dev": true - }, - "through": { - "version": "2.3.8", - "dev": true - }, - "through2": { - "version": "2.0.5", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "to-readable-stream": { - "version": "2.1.0", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "traverse": { - "version": "0.6.6", - "dev": true - }, - "trim-newlines": { - "version": "3.0.0", - "dev": true - }, - "trim-off-newlines": { - "version": "1.0.1", - "dev": true - }, - "type-fest": { - "version": "0.6.0", - "dev": true - }, - "uglify-js": { - "version": "3.12.5", - "dev": true, - "optional": true - }, - "unique-string": { - "version": "2.0.0", - "dev": true, - "requires": { - "crypto-random-string": "^2.0.0" - } - }, - "universal-user-agent": { - "version": "6.0.0", - "dev": true - }, - "universalify": { - "version": "2.0.0", - "dev": true - }, - "url-join": { - "version": "4.0.1", - "dev": true - }, - "util-deprecate": { - "version": "1.0.2", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "wordwrap": { - "version": "1.0.0", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "dev": true - }, - "xml-js": { - "version": "1.6.11", - "dev": true, - "requires": { - "sax": "^1.2.4" - } - }, - "xtend": { - "version": "4.0.2", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "dev": true - }, - "yaml": { - "version": "1.10.0", - "dev": true - }, - "yargs": { - "version": "17.1.1", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.4", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "dev": true - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index ae917d9..0000000 --- a/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "nitride-cil", - "version": "1.0.0", - "private": true, - "scripts": { - "prepare": "husky install", - "build": "dotnet build", - "test": "dotnet test" - }, - "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" - } -} diff --git a/release.config.js b/release.config.js deleted file mode 100644 index 08dda12..0000000 --- a/release.config.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - branches: ["main"], - message: "chore(release): v${nextRelease.version} [skip ci]\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", - ], -}; diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..f357938 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,23 @@ +# Scripts Directory + +This directory contains the basic scripts for working with the library. + +## `setup.sh` + +This verifies the environment is correct and makes sure everything is configured. + +## `build.sh` + +This builds the project and creates the binaries in debug mode. + +## `test.sh` + +This runs any required tests. + +## `format.sh` + +This is used to format the code base using our standards. It matches the commands in the `lefthook` pre-commit hook. + +## `release.sh` + +Intended to run in a CI environment, this creates a NuGet package and publishes it. diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..0cda922 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env sh + +cd $(dirname $0)/.. +./scripts/setup.sh || exit 1 + +dotnet build diff --git a/scripts/format.sh b/scripts/format.sh new file mode 100755 index 0000000..fd015be --- /dev/null +++ b/scripts/format.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env sh + +# Normalize our environment. +cd $(dirname $0)/.. +./scripts/setup.sh || exit 1 + +# Format the .NET code +dotnet format || exit 1 + +# Format using Prettier. +prettier . --write --loglevel warn + +# Format the Flake. +nixfmt flake.nix diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..6cc26bf --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env sh + +cd $(dirname $0)/.. +./scripts/setup.sh || exit 1 + +# Verify the input. +if [ "x$NUGET_TOKEN" = "x" ] +then + echo "the environment variable NUGET_TOKEN is not defined" + exit 1 +fi + +if [ "x$NUGET_PUSH_URL" = "x" ] +then + echo "the environment variable NUGET_PUSH_URL is not defined" + exit 1 +fi + +# Clean up everything from the previous runs. +dotnet clean + +# Version the file based on the Git repository. +(cd src && dotnet dotnet-gitversion /updateprojectfiles) +SEMVER="v$(dotnet gitversion /output json | jq -r .SemVer)" + +if [ "x$SEMVER" = "x" ] +then + echo "cannot figure out the semantic version" + exit 1 +fi + +# Build to pick up the new version. +dotnet build + +# Create and publish the NuGet packages. +dotnet pack --include-symbols --include-source +dotnet nuget push src/*/bin/Debug/*.nupkg --api-key $NUGET_TOKEN --source $NUGET_PUSH_URL + +# Tag and push, but only if we don't have a tag. +if ! git tag | grep $SEMVER >& /dev/null +then + echo "tagging and pushing" + git tag $SEMVER + git push origin $SEMVER +else + echo "not tagging, already exists" +fi diff --git a/scripts/setup.sh b/scripts/setup.sh new file mode 100755 index 0000000..8cb2031 --- /dev/null +++ b/scripts/setup.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env sh + +# Normalize our environment. +cd $(dirname $0)/.. + +# Make sure we have the needed executables installed. +for e in dotnet lefthook prettier nixfmt +do + if ! which $e >& /dev/null + then + echo "Cannot find '$e' in the path" + exit 1 + fi +done + +# Make sure we have lefthook is installed. +lefthook install + +# Make sure our tools are installed. +dotnet tool restore + +# Everything is good. +exit 0 diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..bb106ee --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +cd $(dirname $0)/.. +./scripts/setup.sh || exit 1 + +dotnet test --test-adapter-path:. --logger:"junit;LogFilePath=../artifacts/{assembly}-test-result.xml;MethodFormat=Default;FailureBodyFormat=Verbose" --collect:"XPlat Code Coverage" +dotnet tool run reportgenerator -reports:tests/*/TestResults/*/coverage.cobertura.xml -targetdir:./coverage "-reporttypes:Cobertura;TextSummary" +grep "Line coverage" coverage/Summary.txt diff --git a/shell.nix b/shell.nix deleted file mode 100644 index d6cdd9f..0000000 --- a/shell.nix +++ /dev/null @@ -1,17 +0,0 @@ -let - sources = import ./nix/sources.nix; - pkgs = import sources.nixpkgs {}; -in -pkgs.mkShell { - buildInputs = [ - pkgs.git - pkgs.nodejs-16_x - pkgs.yarn - pkgs.dotnet-sdk_5 - pkgs.niv - ]; - - shellHooks = '' - export DOTNET_CLI_TELEMETRY_OPTOUT=true - ''; -} diff --git a/src/Directory.Build.props b/src/Directory.Build.props index a532a7e..ca9169d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,14 +1,14 @@ - - true - Dylan Moonfire - Moonfire Games - https://gitlab.com/mfgames-cil/nitride-cil - Git - nitride - https://gitlab.com/mfgames-cil/nitride-cil - MIT - + + true + Dylan Moonfire + Moonfire Games + https://gitlab.com/mfgames-cil/nitride-cil + Git + nitride + https://gitlab.com/mfgames-cil/nitride-cil + AGPL-3.0 + diff --git a/src/GitVersion.yml b/src/GitVersion.yml new file mode 100644 index 0000000..5dc355f --- /dev/null +++ b/src/GitVersion.yml @@ -0,0 +1,13 @@ +mode: ContinuousDelivery +increment: Inherit +continuous-delivery-fallback-tag: ci + +major-version-bump-message: "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\([\\w\\s-]*\\))?(!:|:.*\\n\\n((.+\\n)+\\n)?BREAKING CHANGE:\\s.+)" +minor-version-bump-message: "^(feat)(\\([\\w\\s-]*\\))?:" +patch-version-bump-message: "^(build|chore|ci|docs|fix|perf|refactor|revert|style|test)(\\([\\w\\s-]*\\))?:" + +assembly-versioning-scheme: MajorMinorPatch +assembly-file-versioning-scheme: MajorMinorPatch +assembly-informational-format: "{InformationalVersion}" + +tag-prefix: "[vV]" diff --git a/src/Nitride.Calendar/CreateCalendarValidator.cs b/src/Nitride.Calendar/CreateCalendarValidator.cs new file mode 100644 index 0000000..e75e9e6 --- /dev/null +++ b/src/Nitride.Calendar/CreateCalendarValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace Nitride.Calendar; + +public class CreateCalendarValidator : AbstractValidator +{ + public CreateCalendarValidator() + { + this.RuleFor(x => x.Path).NotNull(); + this.RuleFor(x => x.GetEventSummary).NotNull(); + this.RuleFor(x => x.GetEventUrl).NotNull(); + } +} diff --git a/src/Nitride.Calendar/CreateCalender.cs b/src/Nitride.Calendar/CreateCalender.cs index 1049919..eedae3b 100644 --- a/src/Nitride.Calendar/CreateCalender.cs +++ b/src/Nitride.Calendar/CreateCalender.cs @@ -1,104 +1,108 @@ using System; using System.Collections.Generic; using System.Linq; + +using FluentValidation; + 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 +namespace Nitride.Calendar; + +/// +/// 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. +/// +[WithProperties] +public partial class CreateCalender : OperationBase { - /// - /// 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. - /// - [WithProperties] - public partial class CreateCalender : NitrideOperationBase + private readonly Timekeeper clock; + + private readonly IValidator validator; + + public CreateCalender(IValidator validator, Timekeeper clock) { - private readonly NitrideClock clock; + this.validator = validator; + this.clock = clock; + } - public CreateCalender(NitrideClock clock) + /// + /// Gets or sets a callback to get the summary of the event representing + /// the entity. + /// + public Func? GetEventSummary { get; set; } + + /// + /// Gets or sets a callback to get the optional URL of an event for + /// the entity. + /// + public Func? GetEventUrl { get; set; } + + /// + /// Gets or sets the file system path for the resulting calendar. + /// + public UPath? Path { get; set; } + + /// + public override IEnumerable Run(IEnumerable input) + { + this.validator.ValidateAndThrow(this); + + IEnumerable output = input.ForEntities(this.CreateCalendarEntity); + + return output; + } + + private IEnumerable CreateCalendarEntity(IEnumerable 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. + var input = entities.ToList(); + IEnumerable 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) + { + Instant instant = entity.Get(); + var when = this.clock.ToDateTime(instant); + string summary = this.GetEventSummary!(entity); + Uri? url = this.GetEventUrl?.Invoke(entity); + var calendarEvent = new CalendarEvent { - this.clock = clock; - } + Summary = summary, + Start = new CalDateTime(when), + Url = url, + }; - /// - /// Gets or sets a callback to get the summary of the event representing - /// the entity. - /// - public Func? GetEventSummary { get; set; } - - /// - /// Gets or sets a callback to get the optional URL of an event for - /// the entity. - /// - public Func? GetEventUrl { get; set; } - - /// - /// Gets or sets the file system path for the resulting calendar. - /// - public UPath? Path { get; set; } - - /// - public override IEnumerable Run(IEnumerable input) - { - this.CheckNotNull(x => x.Path); - this.CheckNotNull(x => x.GetEventSummary); - - IEnumerable output = input - .ForEntities(this.CreateCalendarEntity); - - return output; - } - - private IEnumerable CreateCalendarEntity( - IEnumerable 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 input = entities.ToList(); - IEnumerable 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(); - 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; - } + return calendarEvent; } } diff --git a/src/Nitride.Calendar/IsCalendar.cs b/src/Nitride.Calendar/IsCalendar.cs index c8e761d..51f0488 100644 --- a/src/Nitride.Calendar/IsCalendar.cs +++ b/src/Nitride.Calendar/IsCalendar.cs @@ -1,10 +1,9 @@ -namespace Nitride.Calendar +namespace Nitride.Calendar; + +/// +/// A marker component for identifying an entity that represents a calendar. +/// +public record IsCalendar { - /// - /// A marker component for identifying an entity that represents a calendar. - /// - public class IsCalendar - { - public static IsCalendar Instance { get; } = new(); - } + public static IsCalendar Instance { get; } = new(); } diff --git a/src/Nitride.Calendar/Nitride.Calendar.csproj b/src/Nitride.Calendar/Nitride.Calendar.csproj index 653a79a..ab1146f 100644 --- a/src/Nitride.Calendar/Nitride.Calendar.csproj +++ b/src/Nitride.Calendar/Nitride.Calendar.csproj @@ -1,37 +1,37 @@ - - net5.0 - enable - + + net6.0 + enable + - - An extension to Nitride static site generator to generate iCalendar files. - + + An extension to Nitride static site generator to generate iCalendar files. + - - - - - + + + + + - - - - - - + + + + + + - - - True - + + + True + - - - Analyzer - False - - + + + Analyzer + False + + diff --git a/src/Nitride.Calendar/NitrideCalendarBuilderExtensions.cs b/src/Nitride.Calendar/NitrideCalendarBuilderExtensions.cs index 57f4e54..a361c7d 100644 --- a/src/Nitride.Calendar/NitrideCalendarBuilderExtensions.cs +++ b/src/Nitride.Calendar/NitrideCalendarBuilderExtensions.cs @@ -1,16 +1,13 @@ using Autofac; + using Nitride.Temporal; -namespace Nitride.Calendar +namespace Nitride.Calendar; + +public static class NitrideCalendarBuilderExtensions { - public static class NitrideCalendarBuilderExtensions + public static NitrideBuilder UseCalendar(this NitrideBuilder builder) { - public static NitrideBuilder UseCalendar(this NitrideBuilder builder) - { - return builder - .UseTemporal() - .ConfigureContainer( - x => x.RegisterModule()); - } + return builder.UseTemporal().ConfigureContainer(x => x.RegisterModule()); } } diff --git a/src/Nitride.Calendar/NitrideCalendarModule.cs b/src/Nitride.Calendar/NitrideCalendarModule.cs index eb2ac45..b3d520b 100644 --- a/src/Nitride.Calendar/NitrideCalendarModule.cs +++ b/src/Nitride.Calendar/NitrideCalendarModule.cs @@ -1,6 +1,13 @@ -namespace Nitride.Calendar +using Autofac; + +namespace Nitride.Calendar; + +public class NitrideCalendarModule : Module { - public class NitrideCalendarModule : NitrideModuleBase + /// + protected override void Load(ContainerBuilder builder) { + builder.RegisterOperators(this); + builder.RegisterValidators(this); } } diff --git a/src/Nitride.Feeds/CreateAtomFeed.cs b/src/Nitride.Feeds/CreateAtomFeed.cs new file mode 100644 index 0000000..828e609 --- /dev/null +++ b/src/Nitride.Feeds/CreateAtomFeed.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using FluentValidation; + +using Gallium; + +using Nitride.Contents; +using Nitride.Feeds.Structure; + +using NodaTime; + +using Serilog; + +using Zio; + +namespace Nitride.Feeds; + +/// +/// Creates various feeds from the given input. +/// +[WithProperties] +public partial class CreateAtomFeed : OperationBase +{ + private readonly ILogger logger; + + private readonly IValidator validator; + + public CreateAtomFeed(ILogger logger, IValidator validator) + { + this.logger = logger; + this.validator = validator; + this.GetAlternateMimeType = _ => "text/html"; + } + + /// + /// Gets or sets the base URL for all the links. + /// + public string? BaseUrl { get; set; } + + /// + /// Gets or sets the alternate MIME type. + /// + public Func GetAlternateMimeType { get; set; } + + /// + /// Gets or sets the alternate URL associated with the feed. + /// + public Func? GetAlternateUrl { get; set; } + + /// + /// Gets or sets the callback to get the author for the feed. + /// + public Func? GetAuthor { get; set; } + + /// + /// Gets or sets the callback to get the entries associated with the + /// feed. + /// + public Func>? GetEntries { get; set; } + + /// + /// Gets or sets the identifier (typically a URL) of the feed. + /// + public Func? GetId { get; set; } + + /// + /// Gets or sets the callback to get the path of the generated feed. + /// + public Func? GetPath { get; set; } + + /// + /// Gets or sets the rights (license) of the feed. + /// + public Func? GetRights { get; set; } + + /// + /// A callback that gets the title of the feed from the given entity. + /// + public Func? GetTitle { get; set; } + + /// + /// Gets or sets the updated timestamp for the feed. + /// + public Func? GetUpdated { get; set; } + + /// + /// Gets or sets the URL associated with the feed. + /// + public Func? GetUrl { get; set; } + + /// + public override IEnumerable Run(IEnumerable input) + { + this.validator.ValidateAndThrow(this); + + return input.SelectMany(this.CreateEntityFeed); + } + + private IEnumerable CreateEntityFeed(Entity entity) + { + // Create the top-level feed. All the nullable callbacks were + // verified in the function that calls this. + var feed = new AtomFeed + { + Title = this.GetTitle?.Invoke(entity), + Id = this.GetId?.Invoke(entity), + Rights = this.GetRights?.Invoke(entity), + Updated = this.GetUpdated?.Invoke(entity), + Url = this.GetUrl?.Invoke(entity), + AlternateUrl = this.GetAlternateUrl?.Invoke(entity), + AlternateMimeType = this.GetAlternateMimeType.Invoke(entity), + Author = this.GetAuthor?.Invoke(entity), + }.ToXElement(); + + // Go through all the items inside the feed and add them. + foreach (AtomEntry? 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 }; + } +} diff --git a/src/Nitride.Feeds/CreateAtomFeedValidator.cs b/src/Nitride.Feeds/CreateAtomFeedValidator.cs new file mode 100644 index 0000000..4f1c43f --- /dev/null +++ b/src/Nitride.Feeds/CreateAtomFeedValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace Nitride.Feeds; + +public class CreateAtomFeedValidator : AbstractValidator +{ + public CreateAtomFeedValidator() + { + this.RuleFor(x => x.GetEntries).NotNull(); + this.RuleFor(x => x.GetPath).NotNull(); + this.RuleFor(x => x.GetTitle).NotNull(); + } +} diff --git a/src/Nitride.Feeds/CreateAtomFeeds.cs b/src/Nitride.Feeds/CreateAtomFeeds.cs deleted file mode 100644 index 865e924..0000000 --- a/src/Nitride.Feeds/CreateAtomFeeds.cs +++ /dev/null @@ -1,123 +0,0 @@ -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 -{ - /// - /// Creates various feeds from the given input. - /// - [WithProperties] - public partial class CreateAtomFeeds : NitrideOperationBase - { - private readonly ILogger logger; - - public CreateAtomFeeds(ILogger logger) - { - this.logger = logger; - this.GetAlternateMimeType = _ => "text/html"; - } - - /// - /// Gets or sets the base URL for all the links. - /// - public string? BaseUrl { get; set; } - - /// - /// Gets or sets the alternate MIME type. - /// - public Func GetAlternateMimeType { get; set; } - - /// - /// Gets or sets the alternate URL associated with the feed. - /// - public Func? GetAlternateUrl { get; set; } - - /// - /// Gets or sets the callback to get the author for the feed. - /// - public Func? GetAuthor { get; set; } - - /// - /// Gets or sets the callback to get the entries associated with the - /// feed. - /// - public Func>? GetEntries { get; set; } - - /// - /// Gets or sets the identifier (typically a URL) of the feed. - /// - public Func? GetId { get; set; } - - /// - /// Gets or sets the callback to get the path of the generated feed. - /// - public Func? GetPath { get; set; } - - /// - /// Gets or sets the rights (license) of the feed. - /// - public Func? GetRights { get; set; } - - /// - /// A callback that gets the title of the feed from the given entity. - /// - public Func? GetTitle { get; set; } - - /// - /// Gets or sets the updated timestamp for the feed. - /// - public Func? GetUpdated { get; set; } - - /// - /// Gets or sets the URL associated with the feed. - /// - public Func? GetUrl { get; set; } - - /// - public override IEnumerable Run(IEnumerable input) - { - this.CheckNotNull(x => this.GetTitle); - this.CheckNotNull(x => this.GetPath); - this.CheckNotNull(x => this.GetEntries); - - return input.SelectMany(this.CreateEntityFeed); - } - - private IEnumerable 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 }; - } - } -} diff --git a/src/Nitride.Feeds/HasFeed.cs b/src/Nitride.Feeds/HasFeed.cs index eaeee87..43298af 100644 --- a/src/Nitride.Feeds/HasFeed.cs +++ b/src/Nitride.Feeds/HasFeed.cs @@ -1,15 +1,14 @@ -namespace Nitride.Feeds -{ - /// - /// A marker component that indicates this entity has a feed associated with - /// it. - /// - public class HasFeed - { - public HasFeed() - { - } +namespace Nitride.Feeds; - public static HasFeed Instance { get; } = new(); +/// +/// A marker component that indicates this entity has a feed associated with +/// it. +/// +public class HasFeed +{ + public HasFeed() + { } + + public static HasFeed Instance { get; } = new(); } diff --git a/src/Nitride.Feeds/IsFeed.cs b/src/Nitride.Feeds/IsFeed.cs index f38927a..d85e0fb 100644 --- a/src/Nitride.Feeds/IsFeed.cs +++ b/src/Nitride.Feeds/IsFeed.cs @@ -1,14 +1,13 @@ -namespace Nitride.Feeds -{ - /// - /// A marker component that indicates this page is a feed. - /// - public class IsFeed - { - public IsFeed() - { - } +namespace Nitride.Feeds; - public static IsFeed Instance { get; } = new(); +/// +/// A marker component that indicates this page is a feed. +/// +public class IsFeed +{ + public IsFeed() + { } + + public static IsFeed Instance { get; } = new(); } diff --git a/src/Nitride.Feeds/Nitride.Feeds.csproj b/src/Nitride.Feeds/Nitride.Feeds.csproj index cbe27f6..9c7215a 100644 --- a/src/Nitride.Feeds/Nitride.Feeds.csproj +++ b/src/Nitride.Feeds/Nitride.Feeds.csproj @@ -1,36 +1,36 @@ - - net5.0 - enable - + + net6.0 + enable + - - An extension to Nitride static site generator to generate Atom feeds. - + + An extension to Nitride static site generator to generate Atom feeds. + - - - - - + + + + + - - - - - + + + + + - - - True - + + + True + - - - Analyzer - False - - + + + Analyzer + False + + diff --git a/src/Nitride.Feeds/NitrideFeedsBuilderExtensions.cs b/src/Nitride.Feeds/NitrideFeedsBuilderExtensions.cs index b9e0ca6..1795c54 100644 --- a/src/Nitride.Feeds/NitrideFeedsBuilderExtensions.cs +++ b/src/Nitride.Feeds/NitrideFeedsBuilderExtensions.cs @@ -1,16 +1,13 @@ using Autofac; + using Nitride.Temporal; -namespace Nitride.Feeds +namespace Nitride.Feeds; + +public static class NitrideFeedsBuilderExtensions { - public static class NitrideFeedsBuilderExtensions + public static NitrideBuilder UseFeeds(this NitrideBuilder builder) { - public static NitrideBuilder UseFeeds(this NitrideBuilder builder) - { - return builder - .UseTemporal() - .ConfigureContainer( - x => x.RegisterModule()); - } + return builder.UseTemporal().ConfigureContainer(x => x.RegisterModule()); } } diff --git a/src/Nitride.Feeds/NitrideFeedsModule.cs b/src/Nitride.Feeds/NitrideFeedsModule.cs index fc5b1ad..b02b693 100644 --- a/src/Nitride.Feeds/NitrideFeedsModule.cs +++ b/src/Nitride.Feeds/NitrideFeedsModule.cs @@ -1,6 +1,13 @@ -namespace Nitride.Feeds +using Autofac; + +namespace Nitride.Feeds; + +public class NitrideFeedsModule : Module { - public class NitrideFeedsModule : NitrideModuleBase + /// + protected override void Load(ContainerBuilder builder) { + builder.RegisterOperators(this); + builder.RegisterValidators(this); } } diff --git a/src/Nitride.Feeds/Structure/AtomAuthor.cs b/src/Nitride.Feeds/Structure/AtomAuthor.cs index 0111417..b504e69 100644 --- a/src/Nitride.Feeds/Structure/AtomAuthor.cs +++ b/src/Nitride.Feeds/Structure/AtomAuthor.cs @@ -1,41 +1,37 @@ using System.Xml.Linq; -namespace Nitride.Feeds.Structure +namespace Nitride.Feeds.Structure; + +/// +/// The type-safe structure for an author element. +/// +[WithProperties] +public partial class AtomAuthor { /// - /// The type-safe structure for an author element. + /// Gets or sets the name of the author. /// - [WithProperties] - public partial class AtomAuthor + public string? Name { get; set; } + + /// + /// Creates an XML element out of the feed along with all items inside + /// the feed. + /// + /// + public XElement? ToXElement() { - /// - /// Gets or sets the name of the author. - /// - public string? Name { get; set; } - - /// - /// Creates an XML element out of the feed along with all items inside - /// the feed. - /// - /// - public XElement? ToXElement() + if (this.Name == null) { - 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; + 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; } } diff --git a/src/Nitride.Feeds/Structure/AtomCategory.cs b/src/Nitride.Feeds/Structure/AtomCategory.cs index 71928b6..72add3f 100644 --- a/src/Nitride.Feeds/Structure/AtomCategory.cs +++ b/src/Nitride.Feeds/Structure/AtomCategory.cs @@ -1,57 +1,53 @@ using System; using System.Xml.Linq; -namespace Nitride.Feeds.Structure +namespace Nitride.Feeds.Structure; + +/// +/// The type-safe structure for a entry's category element. +/// +[WithProperties] +public partial class AtomCategory { /// - /// The type-safe structure for a entry's category element. + /// Gets or sets the label associated with the category. /// - [WithProperties] - public partial class AtomCategory + public string? Label { get; set; } + + /// + /// Gets or sets the scheme associated with the category. + /// + public Uri? Scheme { get; set; } + + /// + /// Gets or sets the term of the category. + /// + public string? Term { get; set; } + + /// + /// Creates an XML element out of the feed along with all items inside + /// the feed. + /// + /// + public XElement ToXElement() { - /// - /// Gets or sets the label associated with the category. - /// - public string? Label { get; set; } - - /// - /// Gets or sets the scheme associated with the category. - /// - public Uri? Scheme { get; set; } - - /// - /// Gets or sets the term of the category. - /// - public string? Term { get; set; } - - /// - /// Creates an XML element out of the feed along with all items inside - /// the feed. - /// - /// - public XElement ToXElement() + if (this.Term == null) { - 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; + 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; } } diff --git a/src/Nitride.Feeds/Structure/AtomEntry.cs b/src/Nitride.Feeds/Structure/AtomEntry.cs index 84b4952..68cc7a6 100644 --- a/src/Nitride.Feeds/Structure/AtomEntry.cs +++ b/src/Nitride.Feeds/Structure/AtomEntry.cs @@ -1,121 +1,119 @@ using System; using System.Collections.Generic; using System.Xml.Linq; + using NodaTime; + using static Nitride.Feeds.Structure.XmlConstants; -namespace Nitride.Feeds.Structure +namespace Nitride.Feeds.Structure; + +/// +/// The type-safe structure for an entry in the Atom feed. +/// +[WithProperties] +public partial class AtomEntry { /// - /// The type-safe structure for an entry in the Atom feed. + /// Gets or sets the author for the feed. /// - [WithProperties] - public partial class AtomEntry + public AtomAuthor? Author { get; set; } + + /// + /// Gets or sets the categories associated with this entry. + /// + public IEnumerable? Categories { get; set; } + + /// + /// Gets or sets the content of the entry. + /// + public string? Content { get; set; } + + /// + /// Gets or sets the type of content (text, html) of the content. + /// + public string ContentType { get; set; } = "html"; + + /// + /// Gets or sets the ID of the feed. + /// + public string? Id { get; set; } + + /// + /// Gets or sets the summary of the entry. + /// + public string? Summary { get; set; } + + /// + /// Gets or sets the type of content (text, html) of the summary. + /// + public string SummaryType { get; set; } = "html"; + + /// + /// Gets or sets the title of the Feed. + /// + public string? Title { get; set; } + + /// + /// Gets or sets the timestamp that the feed was updated. + /// + public Instant? Updated { get; set; } + + /// + /// Gets or sets the URL associated with this feed. + /// + public Uri? Url { get; set; } + + /// + /// Creates an XML element out of the feed along with all items inside + /// the feed. + /// + /// + public XElement? ToXElement() { - /// - /// Gets or sets the author for the feed. - /// - public AtomAuthor? Author { get; set; } + var elem = new XElement(AtomNamespace + "entry"); - /// - /// Gets or sets the categories associated with this entry. - /// - public IEnumerable? Categories { get; set; } + AtomHelper.AddIfSet(elem, "title", this.Title); - /// - /// Gets or sets the content of the entry. - /// - public string? Content { get; set; } - - /// - /// Gets or sets the type of content (text, html) of the content. - /// - public string ContentType { get; set; } = "html"; - - /// - /// Gets or sets the ID of the feed. - /// - public string? Id { get; set; } - - /// - /// Gets or sets the summary of the entry. - /// - public string? Summary { get; set; } - - /// - /// Gets or sets the type of content (text, html) of the summary. - /// - public string SummaryType { get; set; } = "html"; - - /// - /// Gets or sets the title of the Feed. - /// - public string? Title { get; set; } - - /// - /// Gets or sets the timestamp that the feed was updated. - /// - public Instant? Updated { get; set; } - - /// - /// Gets or sets the URL associated with this feed. - /// - public Uri? Url { get; set; } - - /// - /// Creates an XML element out of the feed along with all items inside - /// the feed. - /// - /// - public XElement? ToXElement() + if (this.Url != null) { - 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; + 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 (AtomCategory? 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; } } diff --git a/src/Nitride.Feeds/Structure/AtomFeed.cs b/src/Nitride.Feeds/Structure/AtomFeed.cs index 4a12229..ccbca73 100644 --- a/src/Nitride.Feeds/Structure/AtomFeed.cs +++ b/src/Nitride.Feeds/Structure/AtomFeed.cs @@ -1,104 +1,101 @@ using System; using System.Xml.Linq; + using NodaTime; + using static Nitride.Feeds.Structure.XmlConstants; -namespace Nitride.Feeds.Structure +namespace Nitride.Feeds.Structure; + +/// +/// The type-safe structure of the top-level feed. +/// +public record AtomFeed { /// - /// The type-safe structure of the top-level feed. + /// Gets or sets the MIME type for the alternate URL. /// - [WithProperties] - public partial class AtomFeed + public string AlternateMimeType { get; set; } = "text/html"; + + /// + /// Gets or sets the alternate URL for this feed. + /// + public Uri? AlternateUrl { get; set; } + + /// + /// Gets or sets the author for the feed. + /// + public AtomAuthor? Author { get; set; } + + /// + /// Gets or sets the ID of the feed. + /// + public string? Id { get; set; } + + /// + /// Gets or sets the rights (license) of the feed. + /// + public string? Rights { get; set; } + + /// + /// Gets or sets the title of the Feed. + /// + public string? Title { get; set; } + + /// + /// Gets or sets the timestamp that the feed was updated. + /// + public Instant? Updated { get; set; } + + /// + /// Gets or sets the URL associated with this feed. + /// + public Uri? Url { get; set; } + + /// + /// Creates an XML element out of the feed along with all items inside + /// the feed. + /// + /// + public XElement ToXElement() { - /// - /// Gets or sets the MIME type for the alternate URL. - /// - public string AlternateMimeType { get; set; } = "text/html"; + var elem = new XElement(AtomNamespace + "feed"); - /// - /// Gets or sets the alternate URL for this feed. - /// - public Uri? AlternateUrl { get; set; } - - /// - /// Gets or sets the author for the feed. - /// - public AtomAuthor? Author { get; set; } - - /// - /// Gets or sets the ID of the feed. - /// - public string? Id { get; set; } - - /// - /// Gets or sets the rights (license) of the feed. - /// - public string? Rights { get; set; } - - /// - /// Gets or sets the title of the Feed. - /// - public string? Title { get; set; } - - /// - /// Gets or sets the timestamp that the feed was updated. - /// - public Instant? Updated { get; set; } - - /// - /// Gets or sets the URL associated with this feed. - /// - public Uri? Url { get; set; } - - /// - /// Creates an XML element out of the feed along with all items inside - /// the feed. - /// - /// - public XElement ToXElement() + if (!string.IsNullOrWhiteSpace(this.Title)) { - 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; + 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; } } diff --git a/src/Nitride.Feeds/Structure/AtomHelper.cs b/src/Nitride.Feeds/Structure/AtomHelper.cs index 2c6666a..1a3f751 100644 --- a/src/Nitride.Feeds/Structure/AtomHelper.cs +++ b/src/Nitride.Feeds/Structure/AtomHelper.cs @@ -1,29 +1,25 @@ using System.Xml.Linq; -namespace Nitride.Feeds.Structure -{ - /// - /// Helper methods for working with XML elements. - /// - public static class AtomHelper - { - public static void AddIfSet(XElement root, XElement? elem) - { - if (elem != null) - { - root.Add(elem); - } - } +namespace Nitride.Feeds.Structure; - public static void AddIfSet(XElement elem, string name, string? text) +/// +/// Helper methods for working with XML elements. +/// +public static class AtomHelper +{ + public static void AddIfSet(XElement root, XElement? elem) + { + if (elem != null) { - if (!string.IsNullOrWhiteSpace(text)) - { - elem.Add( - new XElement( - XmlConstants.AtomNamespace + name, - new XText(text))); - } + 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))); } } } diff --git a/src/Nitride.Feeds/Structure/XmlConstants.cs b/src/Nitride.Feeds/Structure/XmlConstants.cs index a315c04..01c8a37 100644 --- a/src/Nitride.Feeds/Structure/XmlConstants.cs +++ b/src/Nitride.Feeds/Structure/XmlConstants.cs @@ -1,22 +1,19 @@ using System.Xml.Linq; -namespace Nitride.Feeds.Structure +namespace Nitride.Feeds.Structure; + +/// +/// Common constants used while generating feeds. +/// +public static class XmlConstants { /// - /// Common constants used while generating feeds. + /// The XML namespace for Atom feeds. /// - public static class XmlConstants - { - /// - /// The XML namespace for Atom feeds. - /// - public static readonly XNamespace AtomNamespace = - "http://www.w3.org/2005/Atom"; + public static readonly XNamespace AtomNamespace = "http://www.w3.org/2005/Atom"; - /// - /// The XML namespace for media. - /// - public static readonly XNamespace MediaNamespace = - "http://search.yahoo.com/mrss/"; - } + /// + /// The XML namespace for media. + /// + public static readonly XNamespace MediaNamespace = "http://search.yahoo.com/mrss/"; } diff --git a/src/Nitride.Gemtext/IsGemtext.cs b/src/Nitride.Gemtext/IsGemtext.cs index 352fa3f..552a52c 100644 --- a/src/Nitride.Gemtext/IsGemtext.cs +++ b/src/Nitride.Gemtext/IsGemtext.cs @@ -1,11 +1,10 @@ -namespace Nitride.Gemtext +namespace Nitride.Gemtext; + +/// +/// A marker component for indicating that an entity is Gemtext, the format +/// for text files using the Gemini protocol. +/// +public record IsGemtext { - /// - /// A marker component for indicating that an entity is Gemtext, the format - /// for text files using the Gemini protocol. - /// - public class IsGemtext - { - public static IsGemtext Instance { get; } = new IsGemtext(); - } + public static IsGemtext Instance { get; } = new(); } diff --git a/src/Nitride.Gemtext/Nitride.Gemtext.csproj b/src/Nitride.Gemtext/Nitride.Gemtext.csproj index cc60e1c..e40f7f0 100644 --- a/src/Nitride.Gemtext/Nitride.Gemtext.csproj +++ b/src/Nitride.Gemtext/Nitride.Gemtext.csproj @@ -1,27 +1,27 @@ - - net5.0 - + + net6.0 + - - An extension to Nitride static site generator to generate Gemtext output. - + + An extension to Nitride static site generator to generate Gemtext output. + - - - + + + - - - True - + + + True + - - - Analyzer - False - - + + + Analyzer + False + + diff --git a/src/Nitride.Gemtext/NitrideGemtextBuilderExtensions.cs b/src/Nitride.Gemtext/NitrideGemtextBuilderExtensions.cs index 19b307a..2147d57 100644 --- a/src/Nitride.Gemtext/NitrideGemtextBuilderExtensions.cs +++ b/src/Nitride.Gemtext/NitrideGemtextBuilderExtensions.cs @@ -1,14 +1,11 @@ using Autofac; -namespace Nitride.Gemtext +namespace Nitride.Gemtext; + +public static class NitrideGemtextBuilderExtensions { - public static class NitrideGemtextBuilderExtensions + public static NitrideBuilder UseGemtext(this NitrideBuilder builder) { - public static NitrideBuilder UseGemtext(this NitrideBuilder builder) - { - return builder - .ConfigureContainer( - x => x.RegisterModule()); - } + return builder.ConfigureContainer(x => x.RegisterModule()); } } diff --git a/src/Nitride.Gemtext/NitrideGemtextModule.cs b/src/Nitride.Gemtext/NitrideGemtextModule.cs index d2866e8..cc8ac47 100644 --- a/src/Nitride.Gemtext/NitrideGemtextModule.cs +++ b/src/Nitride.Gemtext/NitrideGemtextModule.cs @@ -1,6 +1,11 @@ -namespace Nitride.Gemtext +using Autofac; + +namespace Nitride.Gemtext; + +public class NitrideGemtextModule : Module { - public class NitrideGemtextModule : NitrideModuleBase + /// + protected override void Load(ContainerBuilder builder) { } } diff --git a/src/Nitride.Generators/CodeAnalysisExtensions.cs b/src/Nitride.Generators/CodeAnalysisExtensions.cs index afe66cf..b2552b6 100644 --- a/src/Nitride.Generators/CodeAnalysisExtensions.cs +++ b/src/Nitride.Generators/CodeAnalysisExtensions.cs @@ -1,159 +1,140 @@ using Microsoft.CodeAnalysis; -namespace Nitride.Generators +namespace Nitride.Generators; + +/// +/// Various wrappers around the diagnostics to simplify generation. +/// +public static class CodeAnalysisExtensions { /// - /// Various wrappers around the diagnostics to simplify generation. + /// Creates an error message to break the build while generating code. /// - public static class CodeAnalysisExtensions + /// The context that contains the diagnostic. + /// The normalized message code. + /// The string format for the message. + /// The optional parameters. + public static void Error( + this GeneratorExecutionContext context, + MessageCode messageCode, + string format, + params object[] parameters) { - /// - /// Creates an error message to break the build while generating code. - /// - /// The context that contains the diagnostic. - /// The normalized message code. - /// The string format for the message. - /// The optional parameters. - public static void Error( - this GeneratorExecutionContext context, - MessageCode messageCode, - string format, - params object[] parameters) - { - Error(context, messageCode, null, format, parameters); - } + Error(context, messageCode, null, format, parameters); + } - /// - /// Creates an error message to break the build while generating code. - /// - /// The context that contains the diagnostic. - /// The normalized message code. - /// The optional location for the message. - /// The string format for the message. - /// The optional parameters. - public static void Error( - this GeneratorExecutionContext context, - MessageCode messageCode, - Location? location, - string format, - params object[] parameters) - { - context.Message( - messageCode, - location, - DiagnosticSeverity.Error, - format, - parameters); - } + /// + /// Creates an error message to break the build while generating code. + /// + /// The context that contains the diagnostic. + /// The normalized message code. + /// The optional location for the message. + /// The string format for the message. + /// The optional parameters. + public static void Error( + this GeneratorExecutionContext context, + MessageCode messageCode, + Location? location, + string format, + params object[] parameters) + { + context.Message(messageCode, location, DiagnosticSeverity.Error, format, parameters); + } - /// - /// Creates an informational message to break the build while generating code. - /// - /// The context that contains the diagnostic. - /// The normalized message code. - /// The string format for the message. - /// The optional parameters. - public static void Information( - this GeneratorExecutionContext context, - MessageCode messageCode, - string format, - params object[] parameters) - { - Information(context, messageCode, null, format, parameters); - } + /// + /// Creates an informational message to break the build while generating code. + /// + /// The context that contains the diagnostic. + /// The normalized message code. + /// The string format for the message. + /// The optional parameters. + public static void Information( + this GeneratorExecutionContext context, + MessageCode messageCode, + string format, + params object[] parameters) + { + Information(context, messageCode, null, format, parameters); + } - /// - /// Creates an informational message to break the build while generating code. - /// - /// The context that contains the diagnostic. - /// The normalized message code. - /// The optional location for the message. - /// The string format for the message. - /// The optional parameters. - public static void Information( - this GeneratorExecutionContext context, - MessageCode messageCode, - Location? location, - string format, - params object[] parameters) - { - context.Message( - messageCode, - location, - DiagnosticSeverity.Info, - format, - parameters); - } + /// + /// Creates an informational message to break the build while generating code. + /// + /// The context that contains the diagnostic. + /// The normalized message code. + /// The optional location for the message. + /// The string format for the message. + /// The optional parameters. + public static void Information( + this GeneratorExecutionContext context, + MessageCode messageCode, + Location? location, + string format, + params object[] parameters) + { + context.Message(messageCode, location, DiagnosticSeverity.Info, format, parameters); + } - /// - /// Creates a warning message to break the build while generating code. - /// - /// The context that contains the diagnostic. - /// The normalized message code. - /// The string format for the message. - /// The optional parameters. - public static void Warning( - this GeneratorExecutionContext context, - MessageCode messageCode, - string format, - params object[] parameters) - { - Warning(context, messageCode, null, format, parameters); - } + /// + /// Creates a warning message to break the build while generating code. + /// + /// The context that contains the diagnostic. + /// The normalized message code. + /// The string format for the message. + /// The optional parameters. + public static void Warning( + this GeneratorExecutionContext context, + MessageCode messageCode, + string format, + params object[] parameters) + { + Warning(context, messageCode, null, format, parameters); + } - /// - /// Creates a warning message to break the build while generating code. - /// - /// The context that contains the diagnostic. - /// The normalized message code. - /// The optional location for the message. - /// The string format for the message. - /// The optional parameters. - public static void Warning( - this GeneratorExecutionContext context, - MessageCode messageCode, - Location? location, - string format, - params object[] parameters) - { - context.Message( - messageCode, - location, - DiagnosticSeverity.Warning, - format, - parameters); - } + /// + /// Creates a warning message to break the build while generating code. + /// + /// The context that contains the diagnostic. + /// The normalized message code. + /// The optional location for the message. + /// The string format for the message. + /// The optional parameters. + public static void Warning( + this GeneratorExecutionContext context, + MessageCode messageCode, + Location? location, + string format, + params object[] parameters) + { + context.Message(messageCode, location, DiagnosticSeverity.Warning, format, parameters); + } - /// - /// Creates a message to break the build while generating code. - /// - /// The context that contains the diagnostic. - /// The normalized message code. - /// The optional location for the message. - /// The string format for the message. - /// The optional parameters. - /// The severity of the message. - 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)); - } + /// + /// Creates a message to break the build while generating code. + /// + /// The context that contains the diagnostic. + /// The normalized message code. + /// The optional location for the message. + /// The string format for the message. + /// The optional parameters. + /// The severity of the message. + 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)); } } diff --git a/src/Nitride.Generators/MessageCode.cs b/src/Nitride.Generators/MessageCode.cs index 98cc85b..eb44152 100644 --- a/src/Nitride.Generators/MessageCode.cs +++ b/src/Nitride.Generators/MessageCode.cs @@ -1,10 +1,9 @@ -namespace Nitride.Generators +namespace Nitride.Generators; + +/// +/// All the error messages produced by the generators. +/// +public enum MessageCode { - /// - /// All the error messages produced by the generators. - /// - public enum MessageCode - { - Debug = 1, - } + Debug = 1, } diff --git a/src/Nitride.Generators/Nitride.Generators.csproj b/src/Nitride.Generators/Nitride.Generators.csproj index 381ef97..c1d185a 100644 --- a/src/Nitride.Generators/Nitride.Generators.csproj +++ b/src/Nitride.Generators/Nitride.Generators.csproj @@ -1,19 +1,19 @@ - - net5.0 - enable - + + net6.0 + enable + - - Common source generators for Nitride. - + + Common source generators for Nitride. + - - - - - - + + + + + + diff --git a/src/Nitride.Generators/WithPropertyClass.cs b/src/Nitride.Generators/WithPropertyClass.cs new file mode 100644 index 0000000..a68e079 --- /dev/null +++ b/src/Nitride.Generators/WithPropertyClass.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Nitride.Generators; + +/// +/// Internal class that consolidates all of the information needed to generate a +/// file. +/// +internal class WithPropertyClass +{ + /// + /// Gets the syntax for the class declaration. + /// + public ClassDeclarationSyntax ClassDeclaration { get; set; } = null!; + + /// + /// Gets or sets the namespace associated with the class. + /// + public string Namespace { get; set; } = null!; + + /// + /// Gets the using statements that are in the class. + /// + public List UsingDirectiveList { get; set; } = new(); +} diff --git a/src/Nitride.Generators/WithPropertySourceGenerator.cs b/src/Nitride.Generators/WithPropertySourceGenerator.cs index 0037a73..08eb9d8 100644 --- a/src/Nitride.Generators/WithPropertySourceGenerator.cs +++ b/src/Nitride.Generators/WithPropertySourceGenerator.cs @@ -1,209 +1,138 @@ 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 +namespace Nitride.Generators; + +/// +/// Implements a source generator that creates Set* methods for the various +/// properties that also returns the same object for purposes of chaining +/// together calls. +/// +[Generator] +public class WithPropertySourceGenerator : ISourceGenerator { - /// - /// Implements a source generator that creates Set* methods for the various - /// properties that also returns the same object for purposes of chaining - /// together calls. - /// - [Generator] - public class WithPropertySourceGenerator : ISourceGenerator + public void Execute(GeneratorExecutionContext context) { - 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 = (WithPropertySyntaxReceiver?)context.SyntaxReceiver; + + if (syntaxReceiver == null) { - // 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); - } + return; } - public void Initialize(GeneratorInitializationContext context) + // Report any messages. + foreach (string? message in syntaxReceiver.Messages) { - // Register a factory that can create our custom syntax receiver - context.RegisterForSyntaxNotifications( - () => new SyntaxReceiver(context)); + 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); } - private void GenerateClassFile( - GeneratorExecutionContext context, - ClassDeclarationSyntax cds) + // If we didn't find anything, then there is nothing to do. + if (syntaxReceiver.ClassList.Count == 0) { - // Get the namespace. - var nds = (NamespaceDeclarationSyntax?)cds.Parent; + return; + } - if (nds == null) + // Go through each one. + foreach (WithPropertyClass classInfo in syntaxReceiver.ClassList) + { + this.GenerateClassFile(context, classInfo); + } + } + + public void Initialize(GeneratorInitializationContext context) + { + // Register a factory that can create our custom syntax receiver + context.RegisterForSyntaxNotifications(() => new WithPropertySyntaxReceiver(context)); + } + + private void GenerateClassFile(GeneratorExecutionContext context, WithPropertyClass unit) + { + // Pull out some fields. + ClassDeclarationSyntax? cds = unit.ClassDeclaration; + + // Create the partial class. + StringBuilder buffer = new(); + buffer.AppendLine("#nullable enable"); + + // Copy the using statements from the file. + foreach (UsingDirectiveSyntax? uds in unit.UsingDirectiveList) + { + buffer.AppendLine(uds.ToString()); + } + + buffer.AppendLine(); + + // Create the namespace. + buffer.AppendLine($"namespace {unit.Namespace}"); + buffer.AppendLine("{"); + buffer.AppendLine($" public partial class {cds.Identifier}"); + buffer.AppendLine(" {"); + + // Go through the properties of the namespace. + IEnumerable properties = cds.Members + .Where(m => m.Kind() == SyntaxKind.PropertyDeclaration) + .Cast(); + 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) { - return; + continue; } - 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) + // If we aren't first, then add a newline before it. + if (first) + { + first = false; + } + else { - 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(" {"); + // Write some documentation. + buffer.AppendLine(" /// "); + buffer.AppendLine( + string.Format( + " /// Sets the {0} value and returns the operation for chaining.", + pds.Identifier.ToString())); + buffer.AppendLine(" /// "); - // Go through the properties of the namespace. - IEnumerable properties = cds.Members - .Where(m => m.Kind() == SyntaxKind.PropertyDeclaration) - .Cast(); - 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(" /// "); - buffer.AppendLine( - string.Format( - " /// Sets the {0} value and returns the operation for chaining.", - pds.Identifier)); - buffer.AppendLine(" /// "); - - // 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); + // 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(" }"); } - private class SyntaxReceiver : ISyntaxReceiver - { - private readonly GeneratorInitializationContext context; + // Finish up the class. + buffer.AppendLine(" }"); + buffer.AppendLine("}"); - public SyntaxReceiver(GeneratorInitializationContext context) - { - this.context = context; - this.ClassesToAugment = new List(); - this.Messages = new List(); - } - - public List ClassesToAugment { get; } - - public List 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); - } - } - } + // Create the source text and write out the file. + var sourceText = SourceText.From(buffer.ToString(), Encoding.UTF8); + context.AddSource(cds.Identifier + ".Generated.cs", sourceText); } } diff --git a/src/Nitride.Generators/WithPropertySyntaxReceiver.cs b/src/Nitride.Generators/WithPropertySyntaxReceiver.cs new file mode 100644 index 0000000..90135ed --- /dev/null +++ b/src/Nitride.Generators/WithPropertySyntaxReceiver.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; +using System.Linq; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Nitride.Generators; + +internal class WithPropertySyntaxReceiver : ISyntaxReceiver +{ + private readonly GeneratorInitializationContext context; + + public WithPropertySyntaxReceiver(GeneratorInitializationContext context) + { + this.context = context; + this.ClassList = new List(); + this.Messages = new List(); + } + + public List ClassList { get; } + + public List Messages { get; } + + /// + /// Gets or sets the name of the analyzed namespace. + /// + public string? Namespace { get; private set; } + + public List UsingDirectiveList { get; set; } = new(); + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + // Check for namespaces. + switch (syntaxNode) + { + case CompilationUnitSyntax: + // Reset everything. + this.Namespace = null!; + this.UsingDirectiveList = new List(); + break; + case NamespaceDeclarationSyntax syntax: + this.Namespace = syntax.Name.ToString(); + return; + case FileScopedNamespaceDeclarationSyntax syntax: + this.Namespace = syntax.Name.ToString(); + return; + case UsingDirectiveSyntax syntax: + this.UsingDirectiveList.Add(syntax); + return; + case ClassDeclarationSyntax: + break; + default: + return; + } + + // 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.ClassList.Add( + new WithPropertyClass + { + Namespace = this.Namespace!, + UsingDirectiveList = this.UsingDirectiveList, + ClassDeclaration = cds, + }); + } + } +} diff --git a/src/Nitride.Handlebars/ApplyContentHandlebarsTemplate.cs b/src/Nitride.Handlebars/ApplyContentHandlebarsTemplate.cs deleted file mode 100644 index 2f49644..0000000 --- a/src/Nitride.Handlebars/ApplyContentHandlebarsTemplate.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using Gallium; -using HandlebarsDotNet; -using Nitride.Contents; - -namespace Nitride.Handlebars -{ - /// - /// 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. - /// - public class ApplyContentHandlebarsTemplate : NitrideOperationBase - { - private readonly HandlebarsTemplateCache cache; - - public ApplyContentHandlebarsTemplate(HandlebarsTemplateCache cache) - { - this.cache = cache; - } - - /// - /// 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. - /// - public Func? CreateModelCallback { get; set; } - - /// - public override IEnumerable Run(IEnumerable input) - { - this.CheckNotNull(x => x.CreateModelCallback); - - return input - .ForEachEntity(this.Apply); - } - - /// - /// Sets the callback for the template and returns the operation to - /// chain operations. - /// - /// The callback to set. - /// The ApplyContentHandlebarsTemplate to chain requests. - public ApplyContentHandlebarsTemplate WithCreateModelCallback( - Func? 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 template = - this.cache.GetLiteralTemplate(text); - - // Render the template and create a new entity with the updated - // text. - string result = template(model!); - - return entity - .Remove() - .SetTextContent(new StringTextContent(result)); - } - } -} diff --git a/src/Nitride.Handlebars/ApplyStyleHandlebarsTemplate.cs b/src/Nitride.Handlebars/ApplyStyleHandlebarsTemplate.cs deleted file mode 100644 index 84c2278..0000000 --- a/src/Nitride.Handlebars/ApplyStyleHandlebarsTemplate.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Collections.Generic; -using Gallium; -using HandlebarsDotNet; -using Nitride.Contents; - -namespace Nitride.Handlebars -{ - /// - /// An operation that applies a common or shared template on the content of - /// a document that includes theme or styling information. - /// - public class ApplyStyleHandlebarsTemplate : NitrideOperationBase - { - private readonly HandlebarsTemplateCache cache; - - public ApplyStyleHandlebarsTemplate(HandlebarsTemplateCache cache) - { - this.cache = cache; - } - - /// - /// 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. - /// - public Func? CreateModelCallback { get; set; } - - /// - /// 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. - /// - public Func? GetTemplateName { get; set; } - - public IHandlebars? Handlebars { get; set; } - - /// - public override IEnumerable Run(IEnumerable 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(this.Apply); - } - - /// - /// Sets the callback for the template and returns the operation to - /// chain operations. - /// - /// The callback to set. - /// The ApplyContentHandlebarsTemplate to chain requests. - public ApplyStyleHandlebarsTemplate WithCreateModelCallback( - Func? callback) - { - this.CreateModelCallback = callback; - return this; - } - - public ApplyStyleHandlebarsTemplate WithGetTemplateName( - Func? callback) - { - this.GetTemplateName = callback; - return this; - } - - public ApplyStyleHandlebarsTemplate 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 template = - this.cache.GetNamedTemplate(name); - string result = template(model!); - - return entity.SetTextContent(result); - } - } -} diff --git a/src/Nitride.Handlebars/ApplyStyleTemplate.cs b/src/Nitride.Handlebars/ApplyStyleTemplate.cs new file mode 100644 index 0000000..743a7be --- /dev/null +++ b/src/Nitride.Handlebars/ApplyStyleTemplate.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; + +using FluentValidation; + +using Gallium; + +using HandlebarsDotNet; + +using Nitride.Contents; + +namespace Nitride.Handlebars; + +/// +/// An operation that applies a common or shared template on the content of +/// a document that includes theme or styling information. +/// +public class ApplyStyleTemplate : OperationBase +{ + // TODO: This does not use [WithProperties] because the source generator hasn't been taught how to do generics. + + private readonly HandlebarsTemplateCache cache; + + private readonly IValidator> validator; + + public ApplyStyleTemplate(IValidator> validator, HandlebarsTemplateCache cache) + { + this.validator = validator; + this.cache = cache; + } + + /// + /// 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. + /// + public Func? CreateModelCallback { get; set; } + + /// + /// 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. + /// + public Func? GetTemplateName { get; set; } + + public IHandlebars? Handlebars { get; set; } + + /// + public override IEnumerable Run(IEnumerable input) + { + // Make sure we have sane data. + this.validator.ValidateAndThrow(this); + + // Create and set up the Handlebars. + return input.ForEachEntity(this.Apply); + } + + /// + /// Sets the callback for the template and returns the operation to + /// chain operations. + /// + /// The callback to set. + /// The ApplyContentHandlebarsTemplate to chain requests. + public ApplyStyleTemplate WithCreateModelCallback(Func? callback) + { + this.CreateModelCallback = callback; + return this; + } + + public ApplyStyleTemplate WithGetTemplateName(Func? callback) + { + this.GetTemplateName = callback; + return this; + } + + public ApplyStyleTemplate 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 template = this.cache.GetNamedTemplate(name); + string result = template(model!); + + return entity.SetTextContent(result); + } +} diff --git a/src/Nitride.Handlebars/ApplyStyleTemplateValidator.cs b/src/Nitride.Handlebars/ApplyStyleTemplateValidator.cs new file mode 100644 index 0000000..3c52757 --- /dev/null +++ b/src/Nitride.Handlebars/ApplyStyleTemplateValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace Nitride.Handlebars; + +public class ApplyStyleTemplateValidator : AbstractValidator> +{ + public ApplyStyleTemplateValidator() + { + this.RuleFor(x => x.Handlebars).NotNull(); + this.RuleFor(x => x.GetTemplateName).NotNull(); + this.RuleFor(x => x.CreateModelCallback).NotNull(); + } +} diff --git a/src/Nitride.Handlebars/HandlebarsTemplateCache.cs b/src/Nitride.Handlebars/HandlebarsTemplateCache.cs index 3451fbd..eab0a56 100644 --- a/src/Nitride.Handlebars/HandlebarsTemplateCache.cs +++ b/src/Nitride.Handlebars/HandlebarsTemplateCache.cs @@ -1,70 +1,65 @@ using System.Collections.Generic; + using HandlebarsDotNet; + using Open.Threading; -namespace Nitride.Handlebars +namespace Nitride.Handlebars; + +/// +/// Implements a cache for templates to prevent compiling the same template +/// more than once. +/// +public class HandlebarsTemplateCache { - /// - /// Implements a cache for templates to prevent compiling the same template - /// more than once. - /// - public class HandlebarsTemplateCache + private readonly IHandlebars handlebars; + + private readonly ModificationSynchronizer locker; + + private readonly Dictionary> templates; + + public HandlebarsTemplateCache(IHandlebars handlebars) { - private readonly IHandlebars handlebars; + this.handlebars = handlebars; + this.locker = new ModificationSynchronizer(); + this.templates = new Dictionary>(); + } - private readonly ModificationSynchronizer locker; + /// + /// 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. + /// + /// The string that contains the template. + /// + public HandlebarsTemplate GetLiteralTemplate(string literal) + { + // Start with a read lock to see if we've already compiled it. + this.locker.Modifying( + () => !this.templates.ContainsKey(literal), + () => + { + HandlebarsTemplate template = this.handlebars!.Compile(literal); - private readonly Dictionary> - templates; + this.templates[literal] = template; - public HandlebarsTemplateCache(IHandlebars handlebars) - { - this.handlebars = handlebars; - this.locker = new ModificationSynchronizer(); - this.templates = - new Dictionary>(); - } + return true; + }); - /// - /// 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. - /// - /// The string that contains the template. - /// - public HandlebarsTemplate GetLiteralTemplate( - string literal) - { - // Start with a read lock to see if we've already compiled it. - this.locker.Modifying( - () => !this.templates.ContainsKey(literal), - () => - { - HandlebarsTemplate template = - this.handlebars - !.Compile(literal); + return this.locker.Reading(() => this.templates[literal]); + } - this.templates[literal] = template; + /// + /// 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. + /// + /// + /// + public HandlebarsTemplate GetNamedTemplate(string templateName) + { + string template = $"{{{{> {templateName}}}}}"; - return true; - }); - - return this.locker.Reading(() => this.templates[literal]); - } - - /// - /// 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. - /// - /// - /// - public HandlebarsTemplate GetNamedTemplate( - string templateName) - { - string template = $"{{{{> {templateName}}}}}"; - - return this.GetLiteralTemplate(template); - } + return this.GetLiteralTemplate(template); } } diff --git a/src/Nitride.Handlebars/HasHandlebarsTemplate.cs b/src/Nitride.Handlebars/HasHandlebarsTemplate.cs index acedb47..8fd0d8a 100644 --- a/src/Nitride.Handlebars/HasHandlebarsTemplate.cs +++ b/src/Nitride.Handlebars/HasHandlebarsTemplate.cs @@ -1,11 +1,10 @@ -namespace Nitride.Handlebars +namespace Nitride.Handlebars; + +/// +/// A marker component that indicates that a given file with text component +/// has a Handlebars template in it. +/// +public record HasHandlebarsTemplate { - /// - /// A marker component that indicates that a given file with text component - /// has a Handlebars template in it. - /// - public class HasHandlebarsTemplate - { - public static HasHandlebarsTemplate Instance { get; } = new(); - } + public static HasHandlebarsTemplate Instance { get; } = new(); } diff --git a/src/Nitride.Handlebars/IdentifyHandlebars.cs b/src/Nitride.Handlebars/IdentifyHandlebars.cs deleted file mode 100644 index 5a59d6a..0000000 --- a/src/Nitride.Handlebars/IdentifyHandlebars.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Collections.Generic; -using Gallium; -using Nitride.Contents; - -namespace Nitride.Handlebars -{ - /// - /// An operation that discovers which text files have a Handlebars template - /// inside them. - /// - public class IdentifyHandlebars : INitrideOperation - { - /// - public IEnumerable Run(IEnumerable input) - { - return input - .ForEachEntity(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; - } - } -} diff --git a/src/Nitride.Handlebars/IdentifyHandlebarsFromComponent.cs b/src/Nitride.Handlebars/IdentifyHandlebarsFromComponent.cs new file mode 100644 index 0000000..9ef5ffc --- /dev/null +++ b/src/Nitride.Handlebars/IdentifyHandlebarsFromComponent.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using FluentValidation; + +using Gallium; + +namespace Nitride.Handlebars; + +/// +/// An operation that discovers which text files have a Handlebars template +/// based on a component. +/// +[WithProperties] +public partial class IdentifyHandlebarsFromComponent : IOperation +{ + private readonly IValidator validator; + + public IdentifyHandlebarsFromComponent(IValidator validator) + { + this.validator = validator; + } + + public Func HasHandlebarsTest { get; set; } = null!; + + /// + public IEnumerable Run(IEnumerable input) + { + this.validator.ValidateAndThrow(this); + + return input.Select( + (entity) => this.HasHandlebarsTest.Invoke(entity) ? entity.Set(HasHandlebarsTemplate.Instance) : entity); + } +} diff --git a/src/Nitride.Handlebars/IdentifyHandlebarsFromComponentValidator.cs b/src/Nitride.Handlebars/IdentifyHandlebarsFromComponentValidator.cs new file mode 100644 index 0000000..4adf49a --- /dev/null +++ b/src/Nitride.Handlebars/IdentifyHandlebarsFromComponentValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace Nitride.Handlebars; + +public class IdentifyHandlebarsFromComponentValidator : AbstractValidator +{ + public IdentifyHandlebarsFromComponentValidator() + { + this.RuleFor(x => x.HasHandlebarsTest).NotNull(); + } +} diff --git a/src/Nitride.Handlebars/IdentifyHandlebarsFromContent.cs b/src/Nitride.Handlebars/IdentifyHandlebarsFromContent.cs new file mode 100644 index 0000000..5ff423a --- /dev/null +++ b/src/Nitride.Handlebars/IdentifyHandlebarsFromContent.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; + +using Gallium; + +using Nitride.Contents; + +namespace Nitride.Handlebars; + +/// +/// An operation that discovers which text files have a Handlebars template +/// inside them. +/// +public class IdentifyHandlebarsFromContent : IOperation +{ + /// + public IEnumerable Run(IEnumerable input) + { + return input.ForEachEntity(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; + } +} diff --git a/src/Nitride.Handlebars/Nitride.Handlebars.csproj b/src/Nitride.Handlebars/Nitride.Handlebars.csproj index 7865823..0192711 100644 --- a/src/Nitride.Handlebars/Nitride.Handlebars.csproj +++ b/src/Nitride.Handlebars/Nitride.Handlebars.csproj @@ -1,36 +1,36 @@ - - net5.0 - enable - + + net6.0 + enable + - - An extension to Nitride static site generator to style output with Handlebars. - + + An extension to Nitride static site generator to style output with Handlebars. + - - - - - - - + + + + + + + - - - + + + - - - True - + + + True + - - - Analyzer - False - - + + + Analyzer + False + + diff --git a/src/Nitride.Handlebars/NitrideHandlebarsBuilderExtensions.cs b/src/Nitride.Handlebars/NitrideHandlebarsBuilderExtensions.cs index 00b6764..d6c4a2c 100644 --- a/src/Nitride.Handlebars/NitrideHandlebarsBuilderExtensions.cs +++ b/src/Nitride.Handlebars/NitrideHandlebarsBuilderExtensions.cs @@ -1,14 +1,11 @@ using Autofac; -namespace Nitride.Handlebars +namespace Nitride.Handlebars; + +public static class NitrideHandlebarsBuilderExtensions { - public static class NitrideHandlebarsBuilderExtensions + public static NitrideBuilder UseHandlebars(this NitrideBuilder builder) { - public static NitrideBuilder UseHandlebars(this NitrideBuilder builder) - { - return builder - .ConfigureContainer( - x => x.RegisterModule()); - } + return builder.ConfigureContainer(x => x.RegisterModule()); } } diff --git a/src/Nitride.Handlebars/NitrideHandlebarsModule.cs b/src/Nitride.Handlebars/NitrideHandlebarsModule.cs index 9351949..ce69d2e 100644 --- a/src/Nitride.Handlebars/NitrideHandlebarsModule.cs +++ b/src/Nitride.Handlebars/NitrideHandlebarsModule.cs @@ -1,22 +1,17 @@ using Autofac; -namespace Nitride.Handlebars +namespace Nitride.Handlebars; + +public class NitrideHandlebarsModule : Module { - public class NitrideHandlebarsModule : NitrideModuleBase + /// + protected override void Load(ContainerBuilder builder) { - /// - protected override void Register(ContainerBuilder builder) - { - builder - .RegisterType() - .AsSelf() - .SingleInstance(); - builder - .RegisterGeneric(typeof(ApplyContentHandlebarsTemplate<>)) - .As(typeof(ApplyContentHandlebarsTemplate<>)); - builder - .RegisterGeneric(typeof(ApplyStyleHandlebarsTemplate<>)) - .As(typeof(ApplyStyleHandlebarsTemplate<>)); - } + builder.RegisterOperators(this); + builder.RegisterValidators(this); + + builder.RegisterType().AsSelf().SingleInstance(); + builder.RegisterGeneric(typeof(RenderContentTemplate<>)).As(typeof(RenderContentTemplate<>)); + builder.RegisterGeneric(typeof(ApplyStyleTemplate<>)).As(typeof(ApplyStyleTemplate<>)); } } diff --git a/src/Nitride.Handlebars/README.md b/src/Nitride.Handlebars/README.md index 6bcd33f..29aff1b 100644 --- a/src/Nitride.Handlebars/README.md +++ b/src/Nitride.Handlebars/README.md @@ -1,5 +1,4 @@ -Nitride.Handlebars -================== +# Nitride.Handlebars This is a collection of [Handlebars](https://github.com/Handlebars-Net/Handlebars.Net) @@ -8,7 +7,7 @@ this package. ## IHandlebars -This library does *not* configure or register `IHandlebars` but requires it to +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. diff --git a/src/Nitride.Handlebars/RenderContentTemplate.cs b/src/Nitride.Handlebars/RenderContentTemplate.cs new file mode 100644 index 0000000..78497ae --- /dev/null +++ b/src/Nitride.Handlebars/RenderContentTemplate.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; + +using FluentValidation; + +using Gallium; + +using HandlebarsDotNet; + +using Nitride.Contents; + +namespace Nitride.Handlebars; + +/// +/// 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. +/// +public class RenderContentTemplate : OperationBase +{ + private readonly HandlebarsTemplateCache cache; + + // TODO: This does not use [WithProperties] because the source generator hasn't been taught how to do generics. + + private readonly IValidator> validator; + + public RenderContentTemplate(IValidator> validator, HandlebarsTemplateCache cache) + { + this.validator = validator; + this.cache = cache; + } + + /// + /// 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. + /// + public Func? CreateModelCallback { get; set; } + + /// + public override IEnumerable Run(IEnumerable input) + { + this.validator.ValidateAndThrow(this); + + return input.ForEachEntity(this.Apply); + } + + /// + /// Sets the callback for the template and returns the operation to + /// chain operations. + /// + /// The callback to set. + /// The ApplyContentHandlebarsTemplate to chain requests. + public RenderContentTemplate WithCreateModelCallback(Func? 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 template = this.cache.GetLiteralTemplate(text); + + // Render the template and create a new entity with the updated + // text. + string result = template(model!); + + return entity.Remove().SetTextContent(new StringTextContent(result)); + } +} diff --git a/src/Nitride.Handlebars/RenderContentTemplateValidator.cs b/src/Nitride.Handlebars/RenderContentTemplateValidator.cs new file mode 100644 index 0000000..27fa53a --- /dev/null +++ b/src/Nitride.Handlebars/RenderContentTemplateValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace Nitride.Handlebars; + +public class RenderContentTemplateValidator : AbstractValidator> +{ + public RenderContentTemplateValidator() + { + this.RuleFor(x => x.CreateModelCallback).NotNull(); + } +} diff --git a/src/Nitride.Html/ConvertHtmlEntitiesToUnicode.cs b/src/Nitride.Html/ConvertHtmlEntitiesToUnicode.cs new file mode 100644 index 0000000..aeaabc1 --- /dev/null +++ b/src/Nitride.Html/ConvertHtmlEntitiesToUnicode.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Net; + +using Gallium; + +using Nitride.Contents; + +namespace Nitride.Html; + +/// +/// Converts the text input that uses HTML entities and turns them into +/// Unicode variations. +/// +public class ConvertHtmlEntitiesToUnicode : OperationBase +{ + /// + public override IEnumerable Run(IEnumerable input) + { + return input.ForEachEntity(this.ResolveHtmlEntities); + } + + private Entity ResolveHtmlEntities(Entity entity, ITextContent content) + { + string text = content.GetText(); + string resolved = WebUtility.HtmlDecode(text); + + return entity.SetTextContent(resolved); + } +} diff --git a/src/Nitride.Html/HtmlEntitiesToUnicode.cs b/src/Nitride.Html/HtmlEntitiesToUnicode.cs deleted file mode 100644 index a60cd2c..0000000 --- a/src/Nitride.Html/HtmlEntitiesToUnicode.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Generic; -using System.Net; -using Gallium; -using Nitride.Contents; - -namespace Nitride.Html -{ - /// - /// Converts the text input that uses HTML entities and turns them into - /// Unicode variations. - /// - public class HtmlEntitiesToUnicode : NitrideOperationBase - { - /// - public override IEnumerable Run(IEnumerable input) - { - return input - .ForEachEntity(this.ResolveHtmlEntities); - } - - private Entity ResolveHtmlEntities( - Entity entity, - ITextContent content) - { - string text = content.GetText(); - string resolved = WebUtility.HtmlDecode(text); - - return entity.SetTextContent(resolved); - } - } -} diff --git a/src/Nitride.Html/IsHtml.cs b/src/Nitride.Html/IsHtml.cs index 8e687ec..1357492 100644 --- a/src/Nitride.Html/IsHtml.cs +++ b/src/Nitride.Html/IsHtml.cs @@ -1,10 +1,9 @@ -namespace Nitride.Html +namespace Nitride.Html; + +/// +/// A marker component that indicates that the entity is an HTML file. +/// +public record IsHtml { - /// - /// A marker component that indicates that the entity is an HTML file. - /// - public class IsHtml - { - public static IsHtml Instance { get; } = new(); - } + public static IsHtml Instance { get; } = new(); } diff --git a/src/Nitride.Html/Nitride.Html.csproj b/src/Nitride.Html/Nitride.Html.csproj index 7aa6d8e..e9fcfc7 100644 --- a/src/Nitride.Html/Nitride.Html.csproj +++ b/src/Nitride.Html/Nitride.Html.csproj @@ -1,31 +1,31 @@ - - net5.0 - + + net6.0 + - - An extension to Nitride static site generator to generate HTML output. - + + An extension to Nitride static site generator to generate HTML output. + - - - + + + - - - True - + + + True + - - - Analyzer - False - - + + + Analyzer + False + + - - - + + + diff --git a/src/Nitride.Html/NitrideHtmlBuilderExtensions.cs b/src/Nitride.Html/NitrideHtmlBuilderExtensions.cs index 77ae44b..3de18a4 100644 --- a/src/Nitride.Html/NitrideHtmlBuilderExtensions.cs +++ b/src/Nitride.Html/NitrideHtmlBuilderExtensions.cs @@ -1,14 +1,11 @@ using Autofac; -namespace Nitride.Html +namespace Nitride.Html; + +public static class NitrideHtmlBuilderExtensions { - public static class NitrideHtmlBuilderExtensions + public static NitrideBuilder UseHtml(this NitrideBuilder builder) { - public static NitrideBuilder UseHtml(this NitrideBuilder builder) - { - return builder - .ConfigureContainer( - x => x.RegisterModule()); - } + return builder.ConfigureContainer(x => x.RegisterModule()); } } diff --git a/src/Nitride.Html/NitrideHtmlModule.cs b/src/Nitride.Html/NitrideHtmlModule.cs index 0534448..35c2d2e 100644 --- a/src/Nitride.Html/NitrideHtmlModule.cs +++ b/src/Nitride.Html/NitrideHtmlModule.cs @@ -1,6 +1,12 @@ -namespace Nitride.Html +using Autofac; + +namespace Nitride.Html; + +public class NitrideHtmlModule : Module { - public class NitrideHtmlModule : NitrideModuleBase + /// + protected override void Load(ContainerBuilder builder) { + builder.RegisterOperators(this); } } diff --git a/src/Nitride.IO/Contents/FileEntryBinaryContent.cs b/src/Nitride.IO/Contents/FileEntryBinaryContent.cs index aaf16e7..f0218d9 100644 --- a/src/Nitride.IO/Contents/FileEntryBinaryContent.cs +++ b/src/Nitride.IO/Contents/FileEntryBinaryContent.cs @@ -1,35 +1,32 @@ using System.IO; + using Nitride.Contents; + using Zio; -namespace Nitride.IO.Contents +namespace Nitride.IO.Contents; + +/// +/// Contains a wrapper around a file entry to retrieve the binary data. +/// +public class FileEntryBinaryContent : IBinaryContent, ITextContentConvertable { - /// - /// Contains a wrapper around a file entry to retrieve the binary data. - /// - public class FileEntryBinaryContent - : IBinaryContent, ITextContentConvertable + private readonly FileEntry entry; + + public FileEntryBinaryContent(FileEntry entry) { - private readonly FileEntry entry; + this.entry = entry; + } - public FileEntryBinaryContent(FileEntry entry) - { - this.entry = entry; - } + /// + public Stream GetStream() + { + return this.entry.Open(FileMode.Open, FileAccess.Read, FileShare.Read); + } - /// - public Stream GetStream() - { - return this.entry.Open( - FileMode.Open, - FileAccess.Read, - FileShare.Read); - } - - /// - public ITextContent ToTextContent() - { - return new FileEntryTextContent(this.entry); - } + /// + public ITextContent ToTextContent() + { + return new FileEntryTextContent(this.entry); } } diff --git a/src/Nitride.IO/Contents/FileEntryTextContent.cs b/src/Nitride.IO/Contents/FileEntryTextContent.cs index 1e6a4d1..8f58260 100644 --- a/src/Nitride.IO/Contents/FileEntryTextContent.cs +++ b/src/Nitride.IO/Contents/FileEntryTextContent.cs @@ -1,38 +1,33 @@ using System.IO; using System.Text; + using Nitride.Contents; + using Zio; -namespace Nitride.IO.Contents +namespace Nitride.IO.Contents; + +/// +/// Contains a wrapper around a file entry to retrieve text data. +/// +public class FileEntryTextContent : ITextContent, IBinaryContentConvertable { - /// - /// Contains a wrapper around a file entry to retrieve text data. - /// - public class FileEntryTextContent - : ITextContent, IBinaryContentConvertable + private readonly FileEntry entry; + + public FileEntryTextContent(FileEntry entry) { - private readonly FileEntry entry; + this.entry = entry; + } - public FileEntryTextContent(FileEntry entry) - { - this.entry = entry; - } + /// + public TextReader GetReader() + { + return new StreamReader(this.entry.Open(FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8); + } - /// - public TextReader GetReader() - { - return new StreamReader( - this.entry.Open( - FileMode.Open, - FileAccess.Read, - FileShare.Read), - Encoding.UTF8); - } - - /// - public IBinaryContent ToBinaryContent() - { - return new FileEntryBinaryContent(this.entry); - } + /// + public IBinaryContent ToBinaryContent() + { + return new FileEntryBinaryContent(this.entry); } } diff --git a/src/Nitride.IO/Contents/ReadFiles.cs b/src/Nitride.IO/Contents/ReadFiles.cs index dd09433..6c9165e 100644 --- a/src/Nitride.IO/Contents/ReadFiles.cs +++ b/src/Nitride.IO/Contents/ReadFiles.cs @@ -1,89 +1,85 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; + using DotNet.Globbing; + +using FluentValidation; + using Gallium; + using Nitride.Contents; + using Zio; -namespace Nitride.IO.Contents +namespace Nitride.IO.Contents; + +/// +/// A module that reads files from the file system and wraps them in an +/// entity with the following components: UPath, IContent. +/// +[WithProperties] +public partial class ReadFiles : FileSystemOperationBase { - /// - /// A module that reads files from the file system and wraps them in an - /// entity with the following components: UPath, IContent. - /// - public class ReadFiles : FileSystemOperation + private readonly IValidator validator; + + public ReadFiles(IValidator validator, IFileSystem fileSystem) + : base(fileSystem) { - public ReadFiles(IFileSystem fileSystem) - : base(fileSystem) - { - } + this.validator = validator; + } - /// - /// Primary method for creating a read file. - /// - public delegate ReadFiles Factory(IFileSystem fileSystem); + /// + /// Gets or sets the file pattern to retrieve fields. This supports + /// globbing both "*" and "**" patterns such as "**/*". + /// + public string Pattern { get; set; } = null!; - /// - /// Reads all files from the file system and returns them. - /// - /// A populated collection of entities. - public IEnumerable Read( - UPath path = new(), - string searchPattern = "*", - SearchOption search = SearchOption.AllDirectories) - { - // Normalize the path. - path = path == new UPath() ? "/" : path; + /// + /// Reads all files from the file system, filtering them out by the + /// minimatch pattern (as defined by DotNet.Blob). + /// + /// A populated collection of entities. + public IEnumerable Run() + { + return this.Run(Array.Empty()); + } - // Search for the file and wrap the results. - IEnumerable files = this.FileSystem - .EnumerateFileEntries(path, searchPattern, search); - IEnumerable entities = files.Select(this.ToEntity); + /// + /// Reads all files from the file system, filtering them out by the + /// minimatch pattern (as defined by DotNet.Blob). + /// + /// A populated collection of entities. + public override IEnumerable Run(IEnumerable input) + { + this.validator.ValidateAndThrow(this); - return entities; - } + var glob = Glob.Parse(this.Pattern); + IEnumerable files = this.FileSystem.EnumerateFileEntries("/", "*", SearchOption.AllDirectories) + .Where(x => glob.IsMatch(x.Path.ToString())); + IEnumerable entities = files.Select(this.ToEntity); - /// - /// Reads all files from the file system and returns them. - /// - /// A populated collection of entities. - public IEnumerable Read(string glob) - { - Glob parsed = Glob.Parse(glob); + return entities; + } - return this.Read(parsed); - } + public ReadFiles WithFileSystem(IFileSystem value) + { + this.FileSystem = value; + return this; + } - /// - /// Reads all files from the file system, filtering them out by the - /// minimatch pattern (as defined by DotNet.Blob). - /// - /// A populated collection of entities. - public IEnumerable Read(Glob glob) - { - IEnumerable files = this.FileSystem - .EnumerateFileEntries("/", "*.*", SearchOption.AllDirectories) - .Where(x => glob.IsMatch(x.Path.ToString())); - IEnumerable entities = files.Select(this.ToEntity); + /// + /// 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. + /// + /// The Zio file entry. + /// An Entity with appropriate content. + private Entity ToEntity(FileEntry file) + { + Entity entity = new Entity().Set(file.Path).SetBinaryContent(new FileEntryBinaryContent(file)); - return entities; - } - - /// - /// 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. - /// - /// The Zio file entry. - /// An Entity with appropriate content. - private Entity ToEntity(FileEntry file) - { - Entity entity = new Entity() - .Set(file.Path) - .SetBinaryContent(new FileEntryBinaryContent(file)); - - return entity; - } + return entity; } } diff --git a/src/Nitride.IO/Contents/ReadFilesValidator.cs b/src/Nitride.IO/Contents/ReadFilesValidator.cs new file mode 100644 index 0000000..9358db2 --- /dev/null +++ b/src/Nitride.IO/Contents/ReadFilesValidator.cs @@ -0,0 +1,12 @@ +using FluentValidation; + +namespace Nitride.IO.Contents; + +public class ReadFilesValidator : AbstractValidator +{ + public ReadFilesValidator() + { + this.RuleFor(x => x.Pattern).NotNull(); + this.RuleFor(x => x.FileSystem).NotNull(); + } +} diff --git a/src/Nitride.IO/Contents/WriteFiles.cs b/src/Nitride.IO/Contents/WriteFiles.cs index 5e49160..28b51b0 100644 --- a/src/Nitride.IO/Contents/WriteFiles.cs +++ b/src/Nitride.IO/Contents/WriteFiles.cs @@ -3,218 +3,209 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; + +using FluentValidation; + using Gallium; + using Nitride.Contents; + using Serilog; + using Zio; -namespace Nitride.IO.Contents +namespace Nitride.IO.Contents; + +/// +/// An operation that writes out entities to a file system. +/// +[WithProperties] +public partial class WriteFiles : FileSystemOperationBase, IOperation { - /// - /// An operation that writes out entities to a file system. - /// - [WithProperties] - public partial class WriteFiles : FileSystemOperation, INitrideOperation + private readonly IValidator validator; + + private Dictionary> factories; + + public WriteFiles(IValidator validator, ILogger logger, IFileSystem fileSystem) + : base(fileSystem) { - private readonly ILogger logger; - - private Dictionary> factories; - - public WriteFiles( - IFileSystem fileSystem, - ILogger logger) - : base(fileSystem) + this.Logger = logger; + this.validator = validator; + this.TextEncoding = Encoding.UTF8; + this.factories = new Dictionary> { - this.logger = logger.ForContext(); - this.factories = new Dictionary> - { - [typeof(IBinaryContent)] = GetBinaryStream, - [typeof(ITextContent)] = this.GetTextStream, - }; + [typeof(IBinaryContent)] = GetBinaryStream, + [typeof(ITextContent)] = this.GetTextStream, + }; + } + + public ILogger Logger { get; set; } = null!; + + public Dictionary> StreamFactories + { + get => this.factories; + set => this.factories = value ?? throw new ArgumentNullException(nameof(value)); + } + + /// + /// Gets or sets the encoding to force any text output. + /// + public Encoding TextEncoding { get; set; } + + /// + /// 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. + /// + /// The entities to parse. + /// The same list of entities without changes. + public override IEnumerable Run(IEnumerable entities) + { + this.validator.ValidateAndThrow(this); + + IEnumerable results = entities.ForEachEntity(this.Process).ToList(); + + return results; + } + + public WriteFiles WithFileSystem(IFileSystem fileSystem) + { + this.FileSystem = fileSystem; + return this; + } + + 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(); } - /// - /// Primary method for creating a write files operation. - /// - public delegate WriteFiles Factory(IFileSystem fileSystem); + // 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); - public Dictionary> StreamFactories + writer.Write(text); + writer.Flush(); + stream.Position = 0; + + return stream; + } + + /// + /// Internal method for writing out the entity. This handles the + /// registered writers to allow for multiple `IContent` types being + /// written out automatically. + /// + /// The entity to write out. + /// The path of the entity. + /// The entity passed in. + 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()) { - get => this.factories; - set => this.factories = - value ?? throw new ArgumentNullException(nameof(value)); - } - - /// - /// Gets or sets the encoding to force any text output. - /// - public Encoding? TextEncoding { get; set; } - - /// - /// 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. - /// - /// The entities to parse. - /// The same list of entities without changes. - public IEnumerable Run(IEnumerable entities) - { - // We need the `ToList()` here, otherwise it doesn't work. - IEnumerable results = entities - .ForEachEntity(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; - } - - /// - /// Internal method for writing out the entity. This handles the - /// registered writers to allow for multiple `IContent` types being - /// written out automatically. - /// - /// The entity to write out. - /// The path of the entity. - /// The entity passed in. - 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 types = new() { content.GetType() }; - - while (types.Count > 0) - { - // Check to see if we have any of these types. - Func? 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."); - } - - /// - /// Writes out a stream to the given path in the file system. - /// - /// The entity being written out. - /// The path to write out, directories will be created. - /// The stream to write out. - /// The entity passed in. - 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; } + + // First see if we have a factory for the exact type of content. + IContent content = entity.GetContent(); + + if (this.factories.TryGetValue(content.GetType(), out Func? 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 Func? binaryContent)) + { + Stream stream = binaryContent(binaryConvertable.ToBinaryContent()); + + return this.Process(entity, path, stream); + } + + if (content is ITextContentConvertable textConvertable + && this.factories.TryGetValue(typeof(ITextContent), out Func? 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 types = new() { content.GetType() }; + + while (types.Count > 0) + { + // Check to see if we have any of these types. + Func? found = types + .Select(x => this.factories.TryGetValue(x, out Func? 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."); + } + + /// + /// Writes out a stream to the given path in the file system. + /// + /// The entity being written out. + /// The path to write out, directories will be created. + /// The stream to write out. + /// The entity passed in. + 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; } } diff --git a/src/Nitride.IO/Contents/WriteFilesValidator.cs b/src/Nitride.IO/Contents/WriteFilesValidator.cs new file mode 100644 index 0000000..53568e8 --- /dev/null +++ b/src/Nitride.IO/Contents/WriteFilesValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; + +namespace Nitride.IO.Contents; + +public class WriteFilesValidator : AbstractValidator +{ + public WriteFilesValidator() + { + this.RuleFor(x => x.StreamFactories).NotNull(); + this.RuleFor(x => x.TextEncoding).NotNull(); + this.RuleFor(x => x.FileSystem).NotNull(); + this.RuleFor(x => x.Logger).NotNull(); + } +} diff --git a/src/Nitride.IO/Directories/ClearDirectory.cs b/src/Nitride.IO/Directories/ClearDirectory.cs index fe66530..f9bc3cc 100644 --- a/src/Nitride.IO/Directories/ClearDirectory.cs +++ b/src/Nitride.IO/Directories/ClearDirectory.cs @@ -1,82 +1,92 @@ using System; using System.Collections.Generic; + +using FluentValidation; + using Gallium; + using Serilog; + using Zio; -namespace Nitride.IO.Directories +namespace Nitride.IO.Directories; + +/// +/// 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. +/// +[WithProperties] +public partial class ClearDirectory : FileSystemOperationBase, IOperation { - /// - /// 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. - /// - [WithProperties] - public partial class ClearDirectory : FileSystemOperation, INitrideOperation + private readonly IValidator validator; + + public ClearDirectory(IValidator validator, IFileSystem fileSystem, ILogger logger) + : base(fileSystem) { - private readonly ILogger logger; + this.Logger = logger; + this.validator = validator; + } - public ClearDirectory( - ILogger logger, - IFileSystem fileSystem) - : base(fileSystem) + public ILogger Logger { get; set; } + + /// + /// Gets or sets the path of the directory to clear. + /// + public UPath? Path { get; set; } + + public IEnumerable Run() + { + return this.Run(new List()); + } + + /// + public override IEnumerable Run(IEnumerable input) + { + this.validator.ValidateAndThrow(this); + + // This really isn't an input-type of operation, but it can fit + // inside one to keep a pattern. + if (!this.Path.HasValue) { - this.logger = logger.ForContext(); + throw new InvalidOperationException( + nameof(ClearDirectory) + + "cannot be used without setting the path either by the" + + "factory method, the constructor, the property, or " + + "SetPath method."); } - /// - /// Gets or sets the path of the directory to clear. - /// - public UPath? Path { get; set; } + // See if the directory exists. If it doesn't, then we make it. + UPath path = this.Path.Value; - public IEnumerable Run() + if (!this.FileSystem.DirectoryExists(path)) { - return this.Run(new List()); + this.Logger.Information("Creating the directory {Path}", path); + this.FileSystem.CreateDirectory(path); } - /// - public IEnumerable Run(IEnumerable input) + // Clear out the contents. + IEnumerable files = this.FileSystem.EnumerateFiles(path); + IEnumerable directories = this.FileSystem.EnumerateDirectories(path); + + foreach (UPath file in files) { - // 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 files = this.FileSystem.EnumerateFiles(path); - IEnumerable 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; + this.FileSystem.DeleteFile(file); } + + foreach (UPath directory in directories) + { + this.FileSystem.DeleteDirectory(directory, true); + } + + // Just pass the input on. + return input; + } + + public ClearDirectory WithFileSystem(IFileSystem value) + { + this.FileSystem = value; + return this; } } diff --git a/src/Nitride.IO/Directories/ClearDirectoryValidator.cs b/src/Nitride.IO/Directories/ClearDirectoryValidator.cs new file mode 100644 index 0000000..38e8ebb --- /dev/null +++ b/src/Nitride.IO/Directories/ClearDirectoryValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace Nitride.IO.Directories; + +public class ClearDirectoryValidator : AbstractValidator +{ + public ClearDirectoryValidator() + { + this.RuleFor(x => x.Path).NotNull(); + this.RuleFor(x => x.FileSystem).NotNull(); + this.RuleFor(x => x.Logger).NotNull(); + } +} diff --git a/src/Nitride.IO/FileSystemOperation.cs b/src/Nitride.IO/FileSystemOperation.cs deleted file mode 100644 index 5d9a4ba..0000000 --- a/src/Nitride.IO/FileSystemOperation.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Zio; - -namespace Nitride.IO -{ - public abstract class FileSystemOperation - { - protected FileSystemOperation(IFileSystem fileSystem) - { - this.FileSystem = fileSystem; - } - - protected IFileSystem FileSystem { get; } - } -} diff --git a/src/Nitride.IO/FileSystemOperationBase.cs b/src/Nitride.IO/FileSystemOperationBase.cs new file mode 100644 index 0000000..788924b --- /dev/null +++ b/src/Nitride.IO/FileSystemOperationBase.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +using Gallium; + +using Zio; + +namespace Nitride.IO; + +public abstract class FileSystemOperationBase : IOperation +{ + protected FileSystemOperationBase(IFileSystem fileSystem) + { + this.FileSystem = fileSystem; + } + + public IFileSystem FileSystem { get; set; } + + /// + public abstract IEnumerable Run(IEnumerable input); +} diff --git a/src/Nitride.IO/Nitride.IO.csproj b/src/Nitride.IO/Nitride.IO.csproj index e12b0c2..b72e74a 100644 --- a/src/Nitride.IO/Nitride.IO.csproj +++ b/src/Nitride.IO/Nitride.IO.csproj @@ -1,37 +1,35 @@ - - net5.0 - enable - Nitride.IO - + + net6.0 + enable + Nitride.IO + An extension to Nitride static site generator to read and write files. + - - An extension to Nitride static site generator to read and write files. - + + + + + + + + - - - - - - - + + + - - - + + + True + - - - True - - - - - Analyzer - False - - + + + Analyzer + False + + diff --git a/src/Nitride.IO/NitrideIOBuilderExtensions.cs b/src/Nitride.IO/NitrideIOBuilderExtensions.cs index bd2310c..63fc90d 100644 --- a/src/Nitride.IO/NitrideIOBuilderExtensions.cs +++ b/src/Nitride.IO/NitrideIOBuilderExtensions.cs @@ -1,14 +1,11 @@ using Autofac; -namespace Nitride.IO +namespace Nitride.IO; + +public static class NitrideIOBuilderExtensions { - public static class NitrideIOBuilderExtensions + public static NitrideBuilder UseIO(this NitrideBuilder builder) { - public static NitrideBuilder UseIO(this NitrideBuilder builder) - { - return builder - .ConfigureContainer( - x => x.RegisterModule()); - } + return builder.ConfigureContainer(x => x.RegisterModule()); } } diff --git a/src/Nitride.IO/NitrideIOModule.cs b/src/Nitride.IO/NitrideIOModule.cs index 1c6123e..25cc2f6 100644 --- a/src/Nitride.IO/NitrideIOModule.cs +++ b/src/Nitride.IO/NitrideIOModule.cs @@ -1,6 +1,13 @@ -namespace Nitride.IO +using Autofac; + +namespace Nitride.IO; + +public class NitrideIOModule : Module { - public class NitrideIOModule : NitrideModuleBase + /// + protected override void Load(ContainerBuilder builder) { + builder.RegisterOperators(this); + builder.RegisterValidators(this); } } diff --git a/src/Nitride.IO/Paths/AddPathPrefix.cs b/src/Nitride.IO/Paths/AddPathPrefix.cs index e000486..717a532 100644 --- a/src/Nitride.IO/Paths/AddPathPrefix.cs +++ b/src/Nitride.IO/Paths/AddPathPrefix.cs @@ -1,48 +1,41 @@ -using System; using System.Collections.Generic; + +using FluentValidation; + using Gallium; + using Zio; -namespace Nitride.IO.Paths +namespace Nitride.IO.Paths; + +[WithProperties] +public partial class AddPathPrefix : OperationBase { - [WithProperties] - public partial class AddPathPrefix : INitrideOperation + private readonly ReplacePath replacePath; + + private readonly IValidator validator; + + public AddPathPrefix(IValidator validator, ReplacePath replacePath) { - public AddPathPrefix() - { - } + this.validator = validator; + this.replacePath = replacePath; + } - public AddPathPrefix(UPath pathPrefix) - { - this.PathPrefix = pathPrefix; - } + /// + /// Gets or sets the prefix for the path operations. + /// + public UPath? PathPrefix { get; set; } - /// - /// Gets or sets the prefix for the path operations. - /// - public UPath? PathPrefix { get; set; } + public override IEnumerable Run(IEnumerable input) + { + this.validator.ValidateAndThrow(this); - public IEnumerable Run(IEnumerable input) - { - if (this.PathPrefix == null) - { - throw new InvalidOperationException( - nameof(AddPathPrefix) - + ".Prefix was not set " - + "(via constructor, property, or SetPrefix) before " - + "Run() was called."); - } + return this.replacePath.WithReplacement(this.RunReplacement).Run(input); + } - 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; - } + private UPath RunReplacement(Entity _, UPath path) + { + string innerRelativePath = path.ToString().TrimStart('/'); + return this.PathPrefix!.Value / innerRelativePath; } } diff --git a/src/Nitride.IO/Paths/AddPathPrefixValidator.cs b/src/Nitride.IO/Paths/AddPathPrefixValidator.cs new file mode 100644 index 0000000..75ca8b6 --- /dev/null +++ b/src/Nitride.IO/Paths/AddPathPrefixValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace Nitride.IO.Paths; + +public class AddPathPrefixValidator : AbstractValidator +{ + public AddPathPrefixValidator() + { + this.RuleFor(x => x.PathPrefix).NotNull(); + } +} diff --git a/src/Nitride.IO/Paths/ChangePathExtension.cs b/src/Nitride.IO/Paths/ChangePathExtension.cs index 22df27c..7b8cfb7 100644 --- a/src/Nitride.IO/Paths/ChangePathExtension.cs +++ b/src/Nitride.IO/Paths/ChangePathExtension.cs @@ -1,50 +1,43 @@ -using System; using System.Collections.Generic; + +using FluentValidation; + using Gallium; + using Zio; -namespace Nitride.IO.Paths +namespace Nitride.IO.Paths; + +/// +/// Changes the extension of the paths given. +/// +[WithProperties] +public partial class ChangePathExtension : IOperation { - /// - /// Changes the extension of the paths given. - /// - [WithProperties] - public partial class ChangePathExtension : INitrideOperation + private readonly ReplacePath replacePath; + + private readonly IValidator validator; + + public ChangePathExtension(IValidator validator, ReplacePath replacePath) { - public ChangePathExtension() - { - } + this.validator = validator; + this.replacePath = replacePath; + } - public ChangePathExtension(string extension) - { - this.Extension = extension; - } + /// + /// Gets or sets the prefix for the path operations. + /// + public string? Extension { get; set; } - /// - /// Gets or sets the prefix for the path operations. - /// - public string? Extension { get; set; } + public IEnumerable Run(IEnumerable input) + { + this.validator.ValidateAndThrow(this); - public IEnumerable Run(IEnumerable input) - { - if (this.Extension == null) - { - throw new InvalidOperationException( - nameof(AddPathPrefix) - + ".Extension was not set " - + "(via constructor, property, or SetExtension) before " - + "Run() was called."); - } + return this.replacePath.WithReplacement(this.RunReplacement).Run(input); + } - ReplacePaths replacePaths = new ReplacePaths() - .WithReplacement(this.RunReplacement); - - return replacePaths.Run(input); - } - - private UPath RunReplacement(Entity _, UPath path) - { - return path.ChangeExtension(this.Extension!); - } + private UPath RunReplacement(Entity _, UPath path) + { + return path.ChangeExtension(this.Extension!); } } diff --git a/src/Nitride.IO/Paths/ChangePathExtensionValidator.cs b/src/Nitride.IO/Paths/ChangePathExtensionValidator.cs new file mode 100644 index 0000000..0575c51 --- /dev/null +++ b/src/Nitride.IO/Paths/ChangePathExtensionValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace Nitride.IO.Paths; + +public class ChangePathExtensionValidator : AbstractValidator +{ + public ChangePathExtensionValidator() + { + this.RuleFor(x => x.Extension).NotNull(); + } +} diff --git a/src/Nitride.IO/Paths/MoveToIndexPath.cs b/src/Nitride.IO/Paths/MoveToIndexPath.cs new file mode 100644 index 0000000..442e611 --- /dev/null +++ b/src/Nitride.IO/Paths/MoveToIndexPath.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; + +using FluentValidation; + +using Gallium; + +using Zio; + +namespace Nitride.IO.Paths; + +/// +/// Moves various files to indexes of a direction with the base filename. +/// +[WithProperties] +public partial class MoveToIndexPath : OperationBase +{ + private readonly ReplacePath replacePath; + + private readonly IValidator validator; + + public MoveToIndexPath(IValidator validator, ReplacePath replacePath) + { + this.validator = validator; + this.replacePath = replacePath; + this.CanMoveCallback = DefaultCanMoveCallback; + } + + /// + /// 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. + /// + public Func? CanMoveCallback { get; set; } + + /// + /// Default implement of the operation moves .html, .htm, .md, and + /// .markdown files into their indexes. + /// + /// + /// True if the file should move, otherwise false. + public static bool DefaultCanMoveCallback(UPath path) + { + return path.GetExtensionWithDot() switch + { + ".htm" => true, + ".html" => true, + ".md" => true, + ".markdown" => true, + _ => false, + }; + } + + public override IEnumerable Run(IEnumerable input) + { + this.validator.ValidateAndThrow(this); + + return this.replacePath.WithReplacement(this.RunReplacement).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; + } +} diff --git a/src/Nitride.IO/Paths/MoveToIndexPathValidator.cs b/src/Nitride.IO/Paths/MoveToIndexPathValidator.cs new file mode 100644 index 0000000..6e51b90 --- /dev/null +++ b/src/Nitride.IO/Paths/MoveToIndexPathValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace Nitride.IO.Paths; + +public class MoveToIndexPathValidator : AbstractValidator +{ + public MoveToIndexPathValidator() + { + this.RuleFor(x => x.CanMoveCallback).NotNull(); + } +} diff --git a/src/Nitride.IO/Paths/MoveToIndexPaths.cs b/src/Nitride.IO/Paths/MoveToIndexPaths.cs deleted file mode 100644 index 521b172..0000000 --- a/src/Nitride.IO/Paths/MoveToIndexPaths.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Collections.Generic; -using Gallium; -using Zio; - -namespace Nitride.IO.Paths -{ - /// - /// Moves various files to indexes of a direction with the base filename. - /// - [WithProperties] - public partial class MoveToIndexPaths : INitrideOperation - { - public MoveToIndexPaths() - { - this.CanMoveCallback = DefaultCanMoveCallback; - } - - /// - /// 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. - /// - public Func? CanMoveCallback { get; set; } - - /// - /// Default implement of the operation moves .html, .htm, .md, and - /// .markdown files into their indexes. - /// - /// - /// True if the file should move, otherwise false. - public static bool DefaultCanMoveCallback(UPath path) - { - return path.GetExtensionWithDot() switch - { - ".htm" => true, - ".html" => true, - ".md" => true, - ".markdown" => true, - _ => false, - }; - } - - public IEnumerable Run(IEnumerable 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; - } - } -} diff --git a/src/Nitride.IO/Paths/RemovePathPrefix.cs b/src/Nitride.IO/Paths/RemovePathPrefix.cs index 461ef39..cb9402a 100644 --- a/src/Nitride.IO/Paths/RemovePathPrefix.cs +++ b/src/Nitride.IO/Paths/RemovePathPrefix.cs @@ -1,58 +1,51 @@ -using System; using System.Collections.Generic; + +using FluentValidation; + using Gallium; + using Zio; -namespace Nitride.IO.Paths +namespace Nitride.IO.Paths; + +/// +/// An operation that removes a path prefix from the input. +/// +[WithProperties] +public partial class RemovePathPrefix : IOperation { - /// - /// An operation that removes a path prefix from the input. - /// - [WithProperties] - public partial class RemovePathPrefix : INitrideOperation + private readonly ReplacePath replacePath; + + private readonly IValidator validator; + + public RemovePathPrefix(IValidator validator, ReplacePath replacePath) { - public RemovePathPrefix() + this.validator = validator; + this.replacePath = replacePath; + } + + /// + /// Gets or sets the prefix for the path operations. + /// + public UPath PathPrefix { get; set; } + + public IEnumerable Run(IEnumerable input) + { + this.validator.ValidateAndThrow(this); + + return this.replacePath.WithReplacement(this.RunReplacement).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); } - public RemovePathPrefix(UPath pathPrefix) - { - this.PathPrefix = pathPrefix; - } - - /// - /// Gets or sets the prefix for the path operations. - /// - public UPath? PathPrefix { get; set; } - - public IEnumerable Run(IEnumerable 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; - } + return path; } } diff --git a/src/Nitride.IO/Paths/RemovePathPrefixValidator.cs b/src/Nitride.IO/Paths/RemovePathPrefixValidator.cs new file mode 100644 index 0000000..056454e --- /dev/null +++ b/src/Nitride.IO/Paths/RemovePathPrefixValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace Nitride.IO.Paths; + +public class RemovePathPrefixValidator : AbstractValidator +{ + public RemovePathPrefixValidator() + { + this.RuleFor(x => x.PathPrefix).NotNull(); + } +} diff --git a/src/Nitride.IO/Paths/ReplacePath.cs b/src/Nitride.IO/Paths/ReplacePath.cs new file mode 100644 index 0000000..911becf --- /dev/null +++ b/src/Nitride.IO/Paths/ReplacePath.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; + +using FluentValidation; + +using Gallium; + +using Zio; + +namespace Nitride.IO.Paths; + +/// +/// 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. +/// +[WithProperties] +public partial class ReplacePath : IOperation +{ + private readonly IValidator validator; + + public ReplacePath(IValidator validator) + { + this.validator = validator; + } + + /// + /// Gets or sets the replacement callback to alter the paths. + /// + public Func Replacement { get; set; } = null!; + + /// + /// 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. + /// + /// The list of input entities. + /// The output entities. + public IEnumerable Run(IEnumerable input) + { + this.validator.ValidateAndThrow(this); + + return input.ForEachEntity( + (entity, oldPath) => + { + UPath newPath = this.Replacement(entity, oldPath); + + return newPath != oldPath ? entity.Set(newPath) : entity; + }); + } +} diff --git a/src/Nitride.IO/Paths/ReplacePathValidator.cs b/src/Nitride.IO/Paths/ReplacePathValidator.cs new file mode 100644 index 0000000..0fa727d --- /dev/null +++ b/src/Nitride.IO/Paths/ReplacePathValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace Nitride.IO.Paths; + +public class ReplacePathValidator : AbstractValidator +{ + public ReplacePathValidator() + { + this.RuleFor(x => x.Replacement).NotNull(); + } +} diff --git a/src/Nitride.IO/Paths/ReplacePaths.cs b/src/Nitride.IO/Paths/ReplacePaths.cs deleted file mode 100644 index 52d9d83..0000000 --- a/src/Nitride.IO/Paths/ReplacePaths.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using Gallium; -using Zio; - -namespace Nitride.IO.Paths -{ - /// - /// 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. - /// - [WithProperties] - public partial class ReplacePaths : INitrideOperation - { - public ReplacePaths() - { - } - - public ReplacePaths(Func replacement) - { - this.Replacement = replacement; - } - - /// - /// Gets or sets the replacement callback to alter the paths. - /// - public Func? Replacement { get; set; } - - /// - /// 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. - /// - /// The list of input entities. - /// The output entities. - public IEnumerable Run(IEnumerable 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( - (entity, oldPath) => - { - UPath newPath = this.Replacement(entity, oldPath); - - return newPath != oldPath - ? entity.Set(newPath) - : entity; - }); - } - } -} diff --git a/src/Nitride.IO/Paths/UPathExtensions.cs b/src/Nitride.IO/Paths/UPathExtensions.cs index eeafcba..d97b781 100644 --- a/src/Nitride.IO/Paths/UPathExtensions.cs +++ b/src/Nitride.IO/Paths/UPathExtensions.cs @@ -1,41 +1,40 @@ using Zio; -namespace Nitride.IO.Paths +namespace Nitride.IO.Paths; + +/// +/// Extension methods for the UPath class. +/// +public static class UPathExtensions { /// - /// Extension methods for the UPath class. + /// Gets the directory path, which excludes the directory at the end + /// of the path. /// - public static class UPathExtensions + /// The path to manipulate. + /// A normalized path. + public static string GetDirectoryIndexPath(this UPath path) { - /// - /// Gets the directory path, which excludes the directory at the end - /// of the path. - /// - /// The path to manipulate. - /// A normalized path. - public static string GetDirectoryIndexPath(this UPath path) + if (path.GetNameWithoutExtension() == "index") { - if (path.GetNameWithoutExtension() == "index") - { - return path.GetDirectory().ToString().TrimEnd('/') + "/"; - } - - return path.ToString(); + return path.GetDirectory().ToString().TrimEnd('/') + "/"; } - public static string GetParentDirectoryIndexPath(this UPath path) + return path.ToString(); + } + + public static string GetParentDirectoryIndexPath(this UPath path) + { + UPath indexPath = path.GetDirectoryIndexPath(); + UPath parent = indexPath.GetDirectory(); + + if (parent == null!) { - UPath indexPath = path.GetDirectoryIndexPath(); - UPath parent = indexPath.GetDirectory(); - - if (parent == null!) - { - parent = "/"; - } - - string parentPath = parent.ToString().TrimEnd('/') + "/"; - - return parentPath; + parent = "/"; } + + string parentPath = parent.ToString().TrimEnd('/') + "/"; + + return parentPath; } } diff --git a/src/Nitride.IO/README.md b/src/Nitride.IO/README.md index 8835ec0..2b62695 100644 --- a/src/Nitride.IO/README.md +++ b/src/Nitride.IO/README.md @@ -4,9 +4,9 @@ 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 +- Path Normalization +- Disk-Based Content ## File System I/O @@ -28,4 +28,3 @@ 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. - diff --git a/src/Nitride.Javascript/InstallYarn.cs b/src/Nitride.Javascript/InstallYarn.cs deleted file mode 100644 index 5eb2f57..0000000 --- a/src/Nitride.Javascript/InstallYarn.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using Gallium; -using Serilog; -using Zio; - -namespace Nitride.Javascript -{ - /// - /// 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. - /// - [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(); - this.Directory = "/"; - } - - /// - /// 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. - /// - public UPath? Directory { get; set; } - - /// - /// Gets or sets a value indicating whether the install should be - /// forced. - /// - public bool ForceInstall { get; set; } - - /// - public IEnumerable Run(IEnumerable 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; - } - } -} diff --git a/src/Nitride.Javascript/Nitride.Javascript.csproj b/src/Nitride.Javascript/Nitride.Javascript.csproj deleted file mode 100644 index fbbc107..0000000 --- a/src/Nitride.Javascript/Nitride.Javascript.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - - net5.0 - - - - An extension to Nitride static site generator to generate Webpack output. - - - - - - - - - - - - - - - True - - - - - Analyzer - False - - - - diff --git a/src/Nitride.Javascript/NitrideJavascriptBuilderExtensions.cs b/src/Nitride.Javascript/NitrideJavascriptBuilderExtensions.cs deleted file mode 100644 index f1f37a9..0000000 --- a/src/Nitride.Javascript/NitrideJavascriptBuilderExtensions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Autofac; - -namespace Nitride.Javascript -{ - public static class NitrideJavascriptBuilderExtensions - { - public static NitrideBuilder UseJavascript(this NitrideBuilder builder) - { - return builder - .ConfigureContainer( - x => x.RegisterModule()); - } - } -} diff --git a/src/Nitride.Javascript/NitrideJavascriptModule.cs b/src/Nitride.Javascript/NitrideJavascriptModule.cs deleted file mode 100644 index 933a677..0000000 --- a/src/Nitride.Javascript/NitrideJavascriptModule.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Nitride.Javascript -{ - public class NitrideJavascriptModule : NitrideModuleBase - { - } -} diff --git a/src/Nitride.Javascript/RunWebpack.cs b/src/Nitride.Javascript/RunWebpack.cs deleted file mode 100644 index fc5c9be..0000000 --- a/src/Nitride.Javascript/RunWebpack.cs +++ /dev/null @@ -1,115 +0,0 @@ -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 -{ - /// - /// Runs `webpack` in the directory, writing out whatever files need to be - /// written. - /// - [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(); - this.WebpackDirectory = "/"; - } - - /// - /// Gets or sets the directory that contains the output from the - /// webpack execution. - /// - public UPath? OutputDirectory { get; set; } - - /// - /// Gets or sets the directory that the install should be run from. This - /// is typically the same file as the webpack.config.js. - /// - public UPath? WebpackDirectory { get; set; } - - /// - public IEnumerable Run(IEnumerable 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))); - } - } -} diff --git a/src/Nitride.Javascript/YarnHelper.cs b/src/Nitride.Javascript/YarnHelper.cs deleted file mode 100644 index 09a3883..0000000 --- a/src/Nitride.Javascript/YarnHelper.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.IO; - -namespace Nitride.Javascript -{ - /// - /// A helper class for finding how to run or install Yarn. - /// - public static class YarnHelper - { - /// - /// Gets the name of the command to run Yarn. - /// - /// The command to run. - 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; - } - } -} diff --git a/src/Nitride.Markdown/ConvertMarkdownToBase.cs b/src/Nitride.Markdown/ConvertMarkdownToBase.cs new file mode 100644 index 0000000..815b941 --- /dev/null +++ b/src/Nitride.Markdown/ConvertMarkdownToBase.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; + +using FluentValidation; + +using Gallium; + +using Markdig; + +using Nitride.Contents; + +namespace Nitride.Markdown; + +[WithProperties] +public abstract partial class ConvertMarkdownToBase : IOperation +{ + private readonly IValidator validator; + + protected ConvertMarkdownToBase(IValidator validator) + { + this.validator = validator; + } + + /// + /// Gets or sets an additional callback to configure additional features + /// from the baseline Markdown. + /// + public Action? ConfigureMarkdown { get; set; } + + /// + public IEnumerable Run(IEnumerable input) + { + // Validate the inputs. + this.validator.ValidateAndThrow(this); + + // 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( + (entity, _, content) => this.Convert(entity, content, options)); + } + + /// + /// Converts the Markdown file into HTML. + /// + /// The entity to convert. + /// The content for this entity. + /// The markdown pipeline. + /// A converted entity. + protected abstract Entity Convert(Entity entity, ITextContent markdownContent, MarkdownPipeline options); +} diff --git a/src/Nitride.Markdown/ConvertMarkdownToBaseValidator.cs b/src/Nitride.Markdown/ConvertMarkdownToBaseValidator.cs new file mode 100644 index 0000000..6e06006 --- /dev/null +++ b/src/Nitride.Markdown/ConvertMarkdownToBaseValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace Nitride.Markdown; + +public class ConvertMarkdownToBaseValidator : AbstractValidator +{ + public ConvertMarkdownToBaseValidator() + { + this.RuleFor(x => x.ConfigureMarkdown).NotNull(); + } +} diff --git a/src/Nitride.Markdown/ConvertMarkdownToGemtext.cs b/src/Nitride.Markdown/ConvertMarkdownToGemtext.cs new file mode 100644 index 0000000..ef691a0 --- /dev/null +++ b/src/Nitride.Markdown/ConvertMarkdownToGemtext.cs @@ -0,0 +1,43 @@ +using FluentValidation; + +using Gallium; + +using Markdig; + +using MfGames.Markdown.Gemtext; + +using Nitride.Contents; +using Nitride.Gemtext; + +namespace Nitride.Markdown; + +/// +/// 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. +/// +public class ConvertMarkdownToGemtext : ConvertMarkdownToBase +{ + public ConvertMarkdownToGemtext(IValidator validator) + : base(validator) + { + } + + /// + /// Converts the Markdown file into HTML. + /// + /// The entity to convert. + /// The content for this entity. + /// The markdown pipeline. + /// A converted entity. + 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().Set(IsGemtext.Instance); + + return entity; + } +} diff --git a/src/Nitride.Markdown/ConvertMarkdownToHtml.cs b/src/Nitride.Markdown/ConvertMarkdownToHtml.cs new file mode 100644 index 0000000..97ca475 --- /dev/null +++ b/src/Nitride.Markdown/ConvertMarkdownToHtml.cs @@ -0,0 +1,42 @@ +using FluentValidation; + +using Gallium; + +using Markdig; + +using Nitride.Contents; +using Nitride.Html; + +namespace Nitride.Markdown; + +/// +/// Converts the input Markdown files into HTML using Markdig. This only +/// processes files with a text input and the IsMarkdown component. +/// +public class ConvertMarkdownToHtml : ConvertMarkdownToBase +{ + public ConvertMarkdownToHtml(IValidator validator) + : base(validator) + { + } + + /// + /// Converts the Markdown file into HTML. + /// + /// The entity to convert. + /// The content for this entity. + /// The markdown pipeline. + /// A converted entity. + 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().Set(IsHtml.Instance); + + // Return the resulting entity. + return entity; + } +} diff --git a/src/Nitride.Markdown/IdentifyMarkdown.cs b/src/Nitride.Markdown/IdentifyMarkdown.cs index 06150c6..cf90ec8 100644 --- a/src/Nitride.Markdown/IdentifyMarkdown.cs +++ b/src/Nitride.Markdown/IdentifyMarkdown.cs @@ -1,83 +1,88 @@ using System; using System.Collections.Generic; + +using FluentValidation; + using Gallium; + using Nitride.Contents; + using Zio; -namespace Nitride.Markdown +namespace Nitride.Markdown; + +/// +/// 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. +/// +[WithProperties] +public partial class IdentifyMarkdown : IOperation { - /// - /// 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. - /// - [WithProperties] - public partial class IdentifyMarkdown : INitrideOperation + private readonly IValidator validator; + + public IdentifyMarkdown(IValidator validator) { - public IdentifyMarkdown() + this.validator = validator; + this.IsMarkdownTest = DefaultIsMarkdown; + } + + public Func IsMarkdownTest { get; set; } + + /// + public IEnumerable Run(IEnumerable input) + { + this.validator.ValidateAndThrow(this); + + return input.ForEachEntity(this.MarkTextEntities) + .ForEachEntity(this.MarkBinaryEntities); + } + + private static bool DefaultIsMarkdown(Entity entity, UPath path) + { + return (path.GetExtensionWithDot() ?? string.Empty).ToLowerInvariant() switch { - this.IsMarkdownTest = DefaultIsMarkdown; + ".md" => true, + ".markdown" => true, + _ => false, + }; + } + + private Entity MarkBinaryEntities(Entity entity, UPath path, IBinaryContent binary) + { + // If we aren't a Markdown file, then there is nothing + // we can do about that. + if (!this.IsMarkdownTest(entity, path)) + { + return entity; } - public Func IsMarkdownTest { get; set; } - - /// - public IEnumerable Run(IEnumerable input) + // Convert the file as a binary. + if (binary is ITextContentConvertable textConvertable) { - return input - .ForEachEntity( - (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( - (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; - }); + entity = entity.SetTextContent(textConvertable.ToTextContent()).Set(IsMarkdown.Instance); + } + else + { + throw new InvalidOperationException( + "Cannot convert a binary content to a " + "text without ITextContentConvertable."); } - private static bool DefaultIsMarkdown(Entity entity, UPath path) + return entity; + } + + private Entity MarkTextEntities(Entity entity, UPath path, ITextContent _) + { + // If we aren't a Markdown file, then there is nothing + // we can do about that. + if (!this.IsMarkdownTest(entity, path)) { - return (path.GetExtensionWithDot() ?? string.Empty) - .ToLowerInvariant() switch - { - ".md" => true, - ".markdown" => true, - _ => false, - }; + return entity; } + + // We are already text, so just mark it as Markdown. + entity = entity.Set(IsMarkdown.Instance); + + return entity; } } diff --git a/src/Nitride.Markdown/IdentifyMarkdownValidator.cs b/src/Nitride.Markdown/IdentifyMarkdownValidator.cs new file mode 100644 index 0000000..2419d88 --- /dev/null +++ b/src/Nitride.Markdown/IdentifyMarkdownValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace Nitride.Markdown; + +public class IdentifyMarkdownValidator : AbstractValidator +{ + public IdentifyMarkdownValidator() + { + this.RuleFor(x => x.IsMarkdownTest).NotNull(); + } +} diff --git a/src/Nitride.Markdown/IsMarkdown.cs b/src/Nitride.Markdown/IsMarkdown.cs index f714e2d..0127f9b 100644 --- a/src/Nitride.Markdown/IsMarkdown.cs +++ b/src/Nitride.Markdown/IsMarkdown.cs @@ -1,10 +1,9 @@ -namespace Nitride.Markdown +namespace Nitride.Markdown; + +/// +/// A marker class that indicates that the file is a Markdown file. +/// +public record IsMarkdown { - /// - /// A marker class that indicates that the file is a Markdown file. - /// - public class IsMarkdown - { - public static IsMarkdown Instance { get; } = new(); - } + public static IsMarkdown Instance { get; } = new(); } diff --git a/src/Nitride.Markdown/MarkdownOperationBase.cs b/src/Nitride.Markdown/MarkdownOperationBase.cs deleted file mode 100644 index b28319e..0000000 --- a/src/Nitride.Markdown/MarkdownOperationBase.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Collections.Generic; -using Gallium; -using Markdig; -using Nitride.Contents; - -namespace Nitride.Markdown -{ - public abstract class MarkdownOperationBase : INitrideOperation - { - /// - /// Gets or sets an additional callback to configure additional features - /// from the baseline Markdown. - /// - public Action? ConfigureMarkdown { get; set; } - - /// - public IEnumerable Run(IEnumerable 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( - (entity, _, content) => this.Convert( - entity, - content, - options)); - } - - /// - /// Converts the Markdown file into HTML. - /// - /// The entity to convert. - /// The content for this entity. - /// The markdown pipeline. - /// A converted entity. - protected abstract Entity Convert( - Entity entity, - ITextContent markdownContent, - MarkdownPipeline options); - } -} diff --git a/src/Nitride.Markdown/MarkdownToGemtext.cs b/src/Nitride.Markdown/MarkdownToGemtext.cs deleted file mode 100644 index 1476282..0000000 --- a/src/Nitride.Markdown/MarkdownToGemtext.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Gallium; -using Markdig; -using MfGames.Markdown.Gemtext; -using Nitride.Contents; -using Nitride.Gemtext; - -namespace Nitride.Markdown -{ - /// - /// 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. - /// - public class MarkdownToGemtext : MarkdownOperationBase - { - /// - /// Converts the Markdown file into HTML. - /// - /// The entity to convert. - /// The content for this entity. - /// The markdown pipeline. - /// A converted entity. - 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() - .Set(IsGemtext.Instance); - - return entity; - } - } -} diff --git a/src/Nitride.Markdown/MarkdownToHtml.cs b/src/Nitride.Markdown/MarkdownToHtml.cs deleted file mode 100644 index 70dd66d..0000000 --- a/src/Nitride.Markdown/MarkdownToHtml.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Gallium; -using Markdig; -using Nitride.Contents; -using Nitride.Html; - -namespace Nitride.Markdown -{ - /// - /// Converts the input Markdown files into HTML using Markdig. This only - /// processes files with a text input and the IsMarkdown component. - /// - public class MarkdownToHtml : MarkdownOperationBase - { - /// - /// Converts the Markdown file into HTML. - /// - /// The entity to convert. - /// The content for this entity. - /// The markdown pipeline. - /// A converted entity. - 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() - .Set(IsHtml.Instance); - - // Return the resulting entity. - return entity; - } - } -} diff --git a/src/Nitride.Markdown/MarkdownToOperationBaseExtension.cs b/src/Nitride.Markdown/MarkdownToOperationBaseExtension.cs deleted file mode 100644 index ae74163..0000000 --- a/src/Nitride.Markdown/MarkdownToOperationBaseExtension.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using Markdig; - -namespace Nitride.Markdown -{ - /// - /// Extension methods to handle generics while configuring a Markdown - /// operation. - /// - public static class MarkdownToOperationBaseExtension - { - public static T WithConfigureMarkdown( - this T operation, - Action? callback) - where T : MarkdownOperationBase - - { - operation.ConfigureMarkdown = callback; - return operation; - } - } -} diff --git a/src/Nitride.Markdown/Nitride.Markdown.csproj b/src/Nitride.Markdown/Nitride.Markdown.csproj index a3d397a..0a403b3 100644 --- a/src/Nitride.Markdown/Nitride.Markdown.csproj +++ b/src/Nitride.Markdown/Nitride.Markdown.csproj @@ -1,37 +1,37 @@ - - net5.0 - enable - + + net6.0 + enable + - - An extension to Nitride static site generator to render Markdown content. - + + An extension to Nitride static site generator to render Markdown content. + - - - - - + + + + + - - - - - - + + + + + + - - - True - + + + True + - - - Analyzer - False - - + + + Analyzer + False + + diff --git a/src/Nitride.Markdown/NitrideMarkdownBuilderExtensions.cs b/src/Nitride.Markdown/NitrideMarkdownBuilderExtensions.cs index e4e18fe..976ddde 100644 --- a/src/Nitride.Markdown/NitrideMarkdownBuilderExtensions.cs +++ b/src/Nitride.Markdown/NitrideMarkdownBuilderExtensions.cs @@ -1,14 +1,11 @@ using Autofac; -namespace Nitride.Markdown +namespace Nitride.Markdown; + +public static class NitrideMarkdownBuilderExtensions { - public static class NitrideMarkdownBuilderExtensions + public static NitrideBuilder UseMarkdown(this NitrideBuilder builder) { - public static NitrideBuilder UseMarkdown(this NitrideBuilder builder) - { - return builder - .ConfigureContainer( - x => x.RegisterModule()); - } + return builder.ConfigureContainer(x => x.RegisterModule()); } } diff --git a/src/Nitride.Markdown/NitrideMarkdownModule.cs b/src/Nitride.Markdown/NitrideMarkdownModule.cs index fc0e8ff..5c154cf 100644 --- a/src/Nitride.Markdown/NitrideMarkdownModule.cs +++ b/src/Nitride.Markdown/NitrideMarkdownModule.cs @@ -1,6 +1,13 @@ -namespace Nitride.Markdown +using Autofac; + +namespace Nitride.Markdown; + +public class NitrideMarkdownModule : Module { - public class NitrideMarkdownModule : NitrideModuleBase + /// + protected override void Load(ContainerBuilder builder) { + builder.RegisterOperators(this); + builder.RegisterValidators(this); } } diff --git a/src/Nitride.Slugs/ISlugConverter.cs b/src/Nitride.Slugs/ISlugConverter.cs new file mode 100644 index 0000000..2d223b4 --- /dev/null +++ b/src/Nitride.Slugs/ISlugConverter.cs @@ -0,0 +1,14 @@ +namespace Nitride.Slugs; + +/// +/// An interface that provides slugs for various paths. +/// +public interface ISlugConverter +{ + /// + /// Converts the given input into a slug. + /// + /// The input string to normalize. + /// The resulting slug. + string ToSlug(string input); +} diff --git a/src/Nitride.Slugs/ISlugProvider.cs b/src/Nitride.Slugs/ISlugProvider.cs deleted file mode 100644 index 86b8c76..0000000 --- a/src/Nitride.Slugs/ISlugProvider.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Nitride.Slugs -{ - /// - /// An interface that provides slugs for various paths. - /// - public interface ISlugProvider - { - /// - /// Converts the given input into a slug. - /// - /// The input string to normalize. - /// The resulting slug. - string ToSlug(string input); - } -} diff --git a/src/Nitride.Slugs/Nitride.Slugs.csproj b/src/Nitride.Slugs/Nitride.Slugs.csproj index ced4fca..04b2e52 100644 --- a/src/Nitride.Slugs/Nitride.Slugs.csproj +++ b/src/Nitride.Slugs/Nitride.Slugs.csproj @@ -1,31 +1,31 @@ - - net5.0 - + + net6.0 + - - An extension to Nitride static site generator to generate slugs. - + + An extension to Nitride static site generator to generate slugs. + - - - + + + - - - + + + - - - True - + + + True + - - - Analyzer - False - - + + + Analyzer + False + + diff --git a/src/Nitride.Slugs/NitrideSlugsBuilderExtensions.cs b/src/Nitride.Slugs/NitrideSlugsBuilderExtensions.cs index 55d5907..9ba0abd 100644 --- a/src/Nitride.Slugs/NitrideSlugsBuilderExtensions.cs +++ b/src/Nitride.Slugs/NitrideSlugsBuilderExtensions.cs @@ -1,28 +1,23 @@ 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)); - } +namespace Nitride.Slugs; - return builder - .ConfigureContainer( - x => - { - x.RegisterInstance(slugs) - .As() - .SingleInstance(); - x.RegisterModule(); - }); +public static class NitrideSlugsBuilderExtensions +{ + public static NitrideBuilder UseSlugs(this NitrideBuilder builder, ISlugConverter slugs) + { + if (slugs == null) + { + throw new ArgumentNullException(nameof(slugs)); } + + return builder.ConfigureContainer( + x => + { + x.RegisterInstance(slugs).As().SingleInstance(); + x.RegisterModule(); + }); } } diff --git a/src/Nitride.Slugs/NitrideSlugsModule.cs b/src/Nitride.Slugs/NitrideSlugsModule.cs index 255c2b9..2437aa7 100644 --- a/src/Nitride.Slugs/NitrideSlugsModule.cs +++ b/src/Nitride.Slugs/NitrideSlugsModule.cs @@ -1,8 +1,7 @@ using Autofac; -namespace Nitride.Slugs +namespace Nitride.Slugs; + +public class NitrideSlugsModule : Module { - public class NitrideSlugsModule : Module - { - } } diff --git a/src/Nitride.Slugs/SimpleSlugConverter.cs b/src/Nitride.Slugs/SimpleSlugConverter.cs new file mode 100644 index 0000000..4ac3202 --- /dev/null +++ b/src/Nitride.Slugs/SimpleSlugConverter.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +using Slugify; + +namespace Nitride.Slugs; + +/// +/// A default implementation of ISlugProvider. +/// +public class SimpleSlugConverter : ISlugConverter, IEnumerable +{ + private readonly List<(string, string)> replacements; + + public SimpleSlugConverter() + { + this.replacements = new List<(string, string)>(); + } + + public SimpleSlugConverter(IDictionary replacements) + : this() + { + foreach (KeyValuePair pair in replacements) + { + this.Add(pair.Key, pair.Value); + } + } + + /// + /// Adds a replacement for the resulting slug. + /// + /// The text to search for. + /// The replacement string. + public void Add(string search, string replace) + { + this.replacements.Add((search, replace)); + } + + /// + public IEnumerator GetEnumerator() + { + return this.replacements.Select(x => x.Item1).GetEnumerator(); + } + + /// + 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; + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } +} diff --git a/src/Nitride.Slugs/SimpleSlugProvider.cs b/src/Nitride.Slugs/SimpleSlugProvider.cs deleted file mode 100644 index 9685d7c..0000000 --- a/src/Nitride.Slugs/SimpleSlugProvider.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using Slugify; - -namespace Nitride.Slugs -{ - /// - /// A default implementation of ISlugProvider. - /// - public class SimpleSlugProvider : ISlugProvider, IEnumerable - { - private readonly List<(string, string)> replacements; - - public SimpleSlugProvider() - { - this.replacements = new List<(string, string)>(); - } - - public SimpleSlugProvider(IDictionary replacements) - : this() - { - foreach (KeyValuePair pair in replacements) - { - this.Add(pair.Key, pair.Value); - } - } - - /// - /// Adds a replacement for the resulting slug. - /// - /// The text to search for. - /// The replacement string. - public void Add(string search, string replace) - { - this.replacements.Add((search, replace)); - } - - /// - public IEnumerator GetEnumerator() - { - return this.replacements - .Select(x => x.Item1) - .GetEnumerator(); - } - - /// - 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; - } - - /// - IEnumerator IEnumerable.GetEnumerator() - { - return this.GetEnumerator(); - } - } -} diff --git a/src/Nitride.Slugs/UnicodeNormalizingSlugConverter.cs b/src/Nitride.Slugs/UnicodeNormalizingSlugConverter.cs new file mode 100644 index 0000000..dde34ec --- /dev/null +++ b/src/Nitride.Slugs/UnicodeNormalizingSlugConverter.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; + +namespace Nitride.Slugs; + +/// +/// Extends the slug provider to strip out accented characters for +/// a normalized form. +/// +public class UnicodeNormalizingSlugConverter : SimpleSlugConverter +{ + /// + public UnicodeNormalizingSlugConverter() + { + } + + /// + public UnicodeNormalizingSlugConverter(IDictionary replacements) + : base(replacements) + { + } + + /// + 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; + } +} diff --git a/src/Nitride.Slugs/UnicodeNormalizingSlugProvider.cs b/src/Nitride.Slugs/UnicodeNormalizingSlugProvider.cs deleted file mode 100644 index 2b7451b..0000000 --- a/src/Nitride.Slugs/UnicodeNormalizingSlugProvider.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace Nitride.Slugs -{ - /// - /// Extends the slug provider to strip out accented characters for - /// a normalized form. - /// - public class UnicodeNormalizingSlugProvider : SimpleSlugProvider - { - /// - public UnicodeNormalizingSlugProvider() - { - } - - /// - public UnicodeNormalizingSlugProvider( - IDictionary replacements) - : base(replacements) - { - } - - /// - 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; - } - } -} diff --git a/src/Nitride.Temporal/CanExpire.cs b/src/Nitride.Temporal/CanExpire.cs index cd15869..9c35c40 100644 --- a/src/Nitride.Temporal/CanExpire.cs +++ b/src/Nitride.Temporal/CanExpire.cs @@ -1,10 +1,9 @@ -namespace Nitride.Temporal +namespace Nitride.Temporal; + +/// +/// A marker component for identifying a post that can expire. +/// +public class CanExpire { - /// - /// A marker component for identifying a post that can expire. - /// - public class CanExpire - { - public static CanExpire Instance { get; } = new(); - } + public static CanExpire Instance { get; } = new(); } diff --git a/src/Nitride.Temporal/Cli/DatePipelineCommandOption.cs b/src/Nitride.Temporal/Cli/DatePipelineCommandOption.cs new file mode 100644 index 0000000..016550c --- /dev/null +++ b/src/Nitride.Temporal/Cli/DatePipelineCommandOption.cs @@ -0,0 +1,62 @@ +using System; +using System.CommandLine; +using System.CommandLine.Invocation; +using System.Globalization; + +using Nitride.Commands; + +using NodaTime; +using NodaTime.Testing; + +using Serilog; + +namespace Nitride.Temporal.Cli; + +/// +/// A factory to inject the "--date=XXXX-XX-XX" argument into the build +/// and other pipeline commands. +/// +public class DatePipelineCommandOption : IPipelineCommandOption +{ + private readonly Timekeeper clock; + + private readonly ILogger logger; + + public DatePipelineCommandOption(ILogger logger, Timekeeper clock) + { + this.logger = logger.ForContext(); + this.clock = clock; + this.Option = new Option("--date") + { + Description = "Sets the date to something other than now", + ArgumentHelpName = "DATE", + }; + } + + /// + public Option Option { get; } + + /// + public void Handle(InvocationContext context) + { + // If we got a date, then use NodaTime's fake clock to set it so + // everything will use that. + var value = (DateTime?)context.ParseResult.GetValueForOption(this.Option); + + if (value.HasValue && value.Value != DateTime.MinValue) + { + // We have a date, so we need to create a fake clock that has this + // date for the entire run. + Instant instant = this.clock.CreateInstant(value.Value); + + this.clock.Clock = new FakeClock(instant); + } + + // Report the date we are processing. + Instant now = this.clock.Clock.GetCurrentInstant(); + ZonedDateTime dateTime = now.InZone(this.clock.DateTimeZone); + string formatted = dateTime.ToString("yyyy-MM-dd HH:mm:ss x", CultureInfo.CurrentCulture); + + this.logger.Information("Setting date/time to {When:l}", formatted); + } +} diff --git a/src/Nitride.Temporal/Cli/ExpiresPipelineCommandOption.cs b/src/Nitride.Temporal/Cli/ExpiresPipelineCommandOption.cs new file mode 100644 index 0000000..561ff3e --- /dev/null +++ b/src/Nitride.Temporal/Cli/ExpiresPipelineCommandOption.cs @@ -0,0 +1,67 @@ +using System.CommandLine; +using System.CommandLine.Invocation; +using System.Globalization; + +using Nitride.Commands; + +using NodaTime; + +using Serilog; + +using TimeSpanParserUtil; + +namespace Nitride.Temporal.Cli; + +/// +/// A factory to inject the "--expires=XXXX" argument into the build +/// and other pipeline commands. +/// +public class ExpiresPipelineCommandOption : IPipelineCommandOption +{ + private readonly Timekeeper clock; + + private readonly ILogger logger; + + public ExpiresPipelineCommandOption(ILogger logger, Timekeeper clock, string? defaultValue = null) + { + this.logger = logger.ForContext(); + this.clock = clock; + this.Option = new Option("--expires", () => defaultValue) + { + Description = "Sets the expiration time as time before the current date", + ArgumentHelpName = "TIMESPAN", + }; + } + + /// + public Option Option { get; } + + /// + public void Handle(InvocationContext context) + { + // If we have a format, then we are going to set one. If the format + // is blank or never, then we are going to treat it as not + // expiring anything. + string? value = (string?)context.ParseResult.GetValueForOption(this.Option); + + if (value != null && value.ToLowerInvariant() != "never") + { + // Parse the format using TimeSpanParser. + this.clock.Expires = TimeSpanParser.Parse(value); + } + + // If we don't have anything, then just report and we're done. + if (this.clock.Expiration == null) + { + this.logger.Information("No entities will be expired"); + return; + } + + // Figure out when we are going to be expiring. + Instant when = this.clock.Expiration.Value; + ZonedDateTime dateTime = when.InZone(this.clock.DateTimeZone); + string formatted = dateTime.ToString("yyyy-MM-dd HH:mm:ss x", CultureInfo.CurrentCulture); + + this.logger.Information("Expiring entries before {When:l}", formatted); + } +} diff --git a/src/Nitride.Temporal/DatePipelineCommandOption.cs b/src/Nitride.Temporal/DatePipelineCommandOption.cs deleted file mode 100644 index 325f274..0000000 --- a/src/Nitride.Temporal/DatePipelineCommandOption.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.CommandLine; -using System.CommandLine.Invocation; -using System.Globalization; -using Nitride.Commands; -using Nitride.Registration; -using NodaTime; -using NodaTime.Testing; -using Serilog; - -namespace Nitride.Temporal -{ - /// - /// A factory to inject the "--date=XXXX-XX-XX" argument into the build - /// and other pipeline commands. - /// - [NoRegistration] - public class DatePipelineCommandOption : IPipelineCommandOption - { - private readonly NitrideClock clock; - - private readonly ILogger logger; - - public DatePipelineCommandOption( - ILogger logger, - NitrideClock clock) - { - this.logger = logger.ForContext(); - this.clock = clock; - this.Option = new Option("--date") - { - Description = "Sets the date to something other than now", - ArgumentHelpName = "DATE", - }; - } - - /// - public Option Option { get; } - - /// - public void Handle(InvocationContext context) - { - // If we got a date, then use NodaTime's fake clock to set it so - // everything will use that. - var value = (DateTime?)context.ParseResult - .ValueForOption(this.Option); - - if (value.HasValue && value.Value != DateTime.MinValue) - { - // We have a date, so we need to create a fake clock that has this - // date for the entire run. - Instant instant = this.clock.CreateInstant(value.Value); - - this.clock.Clock = new FakeClock(instant); - } - - // Report the date we are processing. - Instant now = this.clock.Clock.GetCurrentInstant(); - ZonedDateTime dateTime = now.InZone(this.clock.DateTimeZone); - string formatted = dateTime.ToString( - "yyyy-MM-dd HH:mm:ss x", - CultureInfo.CurrentCulture); - - this.logger.Information( - "Setting date/time to {When:l}", - formatted); - } - } -} diff --git a/src/Nitride.Temporal/ExpiresPipelineCommandOption.cs b/src/Nitride.Temporal/ExpiresPipelineCommandOption.cs deleted file mode 100644 index 4b283b9..0000000 --- a/src/Nitride.Temporal/ExpiresPipelineCommandOption.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.CommandLine; -using System.CommandLine.Invocation; -using System.Globalization; -using Nitride.Commands; -using Nitride.Registration; -using NodaTime; -using Serilog; -using TimeSpanParserUtil; - -namespace Nitride.Temporal -{ - /// - /// A factory to inject the "--expires=XXXX" argument into the build - /// and other pipeline commands. - /// - [NoRegistration] - public class ExpiresPipelineCommandOption : IPipelineCommandOption - { - private readonly NitrideClock clock; - - private readonly ILogger logger; - - public ExpiresPipelineCommandOption( - ILogger logger, - NitrideClock clock, - string? defaultValue = null) - { - this.logger = logger.ForContext(); - this.clock = clock; - this.Option = new Option("--expires", () => defaultValue) - { - Description = - "Sets the expiration time as time before the current date", - ArgumentHelpName = "TIMESPAN", - }; - } - - /// - public Option Option { get; } - - /// - public void Handle(InvocationContext context) - { - // If we have a format, then we are going to set one. If the format - // is blank or never, then we are going to treat it as not - // expiring anything. - string? value = (string?)context.ParseResult - .ValueForOption(this.Option); - - if (value != null && value.ToLowerInvariant() != "never") - { - // Parse the format using TimeSpanParser. - this.clock.Expires = TimeSpanParser.Parse(value); - } - - // If we don't have anything, then just report and we're done. - if (this.clock.Expiration == null) - { - this.logger.Information("No entities will be expired"); - return; - } - - // Figure out when we are going to be expiring. - Instant when = this.clock.Expiration.Value; - ZonedDateTime dateTime = when.InZone(this.clock.DateTimeZone); - string formatted = dateTime.ToString( - "yyyy-MM-dd HH:mm:ss x", - CultureInfo.CurrentCulture); - - this.logger.Information( - "Expiring entries before {When:l}", - formatted); - } - } -} diff --git a/src/Nitride.Temporal/FilterOutExpiredInstant.cs b/src/Nitride.Temporal/FilterOutExpiredInstant.cs new file mode 100644 index 0000000..0683df6 --- /dev/null +++ b/src/Nitride.Temporal/FilterOutExpiredInstant.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using System.Linq; + +using FluentValidation; + +using Gallium; + +using NodaTime; + +namespace Nitride.Temporal; + +/// +/// Filters out all expired entities that are before the expiration. +/// +[WithProperties] +public partial class FilterOutExpiredInstant : OperationBase +{ + private readonly IValidator validator; + + public FilterOutExpiredInstant(IValidator validator, Timekeeper clock) + { + this.validator = validator; + this.Timekeeper = clock; + } + + public Timekeeper Timekeeper { get; set; } + + /// + public override IEnumerable Run(IEnumerable input) + { + this.validator.ValidateAndThrow(this); + + if (!this.Timekeeper.Expiration.HasValue) + { + return input; + } + + return input.ForEntities(x => x.Where(this.IsNotExpired)); + } + + private bool IsNotExpired(Entity entity) + { + Instant instant = entity.Get(); + Instant expiration = this.Timekeeper.Expiration!.Value; + bool isExpired = instant.CompareTo(expiration) < 0; + + return !isExpired; + } +} diff --git a/src/Nitride.Temporal/FilterOutExpiredInstantValidator.cs b/src/Nitride.Temporal/FilterOutExpiredInstantValidator.cs new file mode 100644 index 0000000..c03d527 --- /dev/null +++ b/src/Nitride.Temporal/FilterOutExpiredInstantValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace Nitride.Temporal; + +public class FilterOutExpiredInstantValidator : AbstractValidator +{ + public FilterOutExpiredInstantValidator() + { + this.RuleFor(x => x.Timekeeper).NotNull(); + } +} diff --git a/src/Nitride.Temporal/FilterOutExpiredInstants.cs b/src/Nitride.Temporal/FilterOutExpiredInstants.cs deleted file mode 100644 index d3452b3..0000000 --- a/src/Nitride.Temporal/FilterOutExpiredInstants.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Gallium; -using NodaTime; - -namespace Nitride.Temporal -{ - /// - /// Filters out all expired entities that are before the expiration. - /// - public class FilterOutExpiredInstants : NitrideOperationBase - { - private readonly NitrideClock clock; - - public FilterOutExpiredInstants(NitrideClock clock) - { - this.clock = clock; - } - - /// - public override IEnumerable Run(IEnumerable input) - { - if (!this.clock.Expiration.HasValue) - { - return input; - } - - return input - .OrderBy(x => x.Get()) - .ForEntities( - x => x - .Where(this.IsNotExpired)); - } - - private bool IsNotExpired(Entity entity) - { - var instant = entity.Get(); - Instant expiration = this.clock.Expiration!.Value; - bool isExpired = instant.CompareTo(expiration) < 0; - - return !isExpired; - } - } -} diff --git a/src/Nitride.Temporal/FilterOutFutureInstant.cs b/src/Nitride.Temporal/FilterOutFutureInstant.cs new file mode 100644 index 0000000..5040e8c --- /dev/null +++ b/src/Nitride.Temporal/FilterOutFutureInstant.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; + +using Gallium; + +using NodaTime; + +namespace Nitride.Temporal; + +/// +/// Filters out all entities that have an instant before the current +/// NitrideClock time. +/// +[WithProperties] +public partial class FilterOutFutureInstant : OperationBase +{ + public FilterOutFutureInstant(Timekeeper timekeeper) + { + this.Timekeeper = timekeeper; + } + + public Timekeeper Timekeeper { get; set; } + + /// + public override IEnumerable Run(IEnumerable input) + { + Instant now = this.Timekeeper.Clock.GetCurrentInstant(); + + return input.WhereEntities((_, instant) => instant.CompareTo(now) <= 0); + } +} diff --git a/src/Nitride.Temporal/FilterOutFutureInstantValidator.cs b/src/Nitride.Temporal/FilterOutFutureInstantValidator.cs new file mode 100644 index 0000000..fbb5d49 --- /dev/null +++ b/src/Nitride.Temporal/FilterOutFutureInstantValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace Nitride.Temporal; + +public class FilterOutFutureInstantValidator : AbstractValidator +{ + public FilterOutFutureInstantValidator() + { + this.RuleFor(x => x.Timekeeper).NotNull(); + } +} diff --git a/src/Nitride.Temporal/FilterOutFutureInstants.cs b/src/Nitride.Temporal/FilterOutFutureInstants.cs deleted file mode 100644 index e672cae..0000000 --- a/src/Nitride.Temporal/FilterOutFutureInstants.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; -using Gallium; -using NodaTime; - -namespace Nitride.Temporal -{ - /// - /// Filters out all entities that have an instant before the current - /// NitrideClock time. - /// - public class FilterOutFutureInstants : NitrideOperationBase - { - private readonly NitrideClock clock; - - public FilterOutFutureInstants(NitrideClock clock) - { - this.clock = clock; - } - - /// - public override IEnumerable Run(IEnumerable input) - { - Instant now = this.clock.Clock.GetCurrentInstant(); - - return input - .WhereEntities( - (_, instant) => instant.CompareTo(now) <= 0); - } - } -} diff --git a/src/Nitride.Temporal/Nitride.Temporal.csproj b/src/Nitride.Temporal/Nitride.Temporal.csproj index 5fd6c9d..686f311 100644 --- a/src/Nitride.Temporal/Nitride.Temporal.csproj +++ b/src/Nitride.Temporal/Nitride.Temporal.csproj @@ -1,40 +1,40 @@ - - net5.0 - enable - Nitride.Temporal - + + net6.0 + enable + Nitride.Temporal + - - An extension to Nitride static site generator to add time-based components and filters. - + + An extension to Nitride static site generator to add time-based components and filters. + - - - - - - - - - - + + + + + + + + + + - - - + + + - - - True - + + + True + - - - Analyzer - False - - + + + Analyzer + False + + diff --git a/src/Nitride.Temporal/NitrideClock.cs b/src/Nitride.Temporal/NitrideClock.cs deleted file mode 100644 index 77d929d..0000000 --- a/src/Nitride.Temporal/NitrideClock.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System; -using Nitride.Registration; -using NodaTime; -using NodaTime.Testing; - -namespace Nitride.Temporal -{ - /// - /// A class that handles date and time processing. This provides identifying - /// the desire time zone along with various methods for processing parsed - /// DateTime objects into NodaTime.Instant. - /// - [Singleton] - public class NitrideClock - { - public NitrideClock() - { - // We use FakeClock because we don't want time to advance in the - // middle of running, just in case we are just a few seconds before - // rollover. - Instant instant = SystemClock.Instance.GetCurrentInstant(); - - this.DateTimeZone = DateTimeZoneProviders.Bcl.GetSystemDefault(); - this.Clock = new FakeClock(instant); - } - - /// - /// Gets or sets the clock used to perform time calculations. - /// - public IClock Clock { get; set; } - - /// - /// Gets or sets the date time zone used for processing. - /// - public DateTimeZone DateTimeZone { get; set; } - - /// - /// Gets the instant when the files expire. - /// - public Instant? Expiration => this.Expires.HasValue - ? this.Clock.GetCurrentInstant() - .Minus(Duration.FromTimeSpan(this.Expires.Value)) - : null; - - /// - /// Gets or sets the expiration time span, which is calculated from the - /// current time. - /// - public TimeSpan? Expires { get; set; } - - /// - /// Creates an instant from a given year, month, and day. - /// - /// The numerical year. - /// The numerical month. - /// The numerical day of month. - /// An instant in the clock's time zone. - public Instant CreateInstant(int year, int month, int day) - { - var date = new LocalDate(year, month, day); - - return this.CreateInstant(date); - } - - /// - /// Creates an instant from the given local date. - /// - /// The date to parse. - /// An instant in the clock's time zone. - public Instant CreateInstant(LocalDate date) - { - ZonedDateTime zoned = date.AtStartOfDayInZone(this.DateTimeZone); - var instant = zoned.ToInstant(); - - return instant; - } - - /// - /// Creates an instant from the given date time zone. - /// - /// The object to convert. - /// An instant representing this DateTimeOffset. - public Instant CreateInstant(DateTimeOffset when) - { - OffsetDateTime offset = OffsetDateTime.FromDateTimeOffset(when); - - return offset.ToInstant(); - } - - /// - /// Creates an instant from the given date time. - /// - /// The object to convert. - /// An instant representing this DateTimeOffset. - public Instant CreateInstant(DateTime when) - { - if (when.Kind == DateTimeKind.Unspecified) - { - LocalDate localDate = LocalDate.FromDateTime(when); - - return this.CreateInstant(localDate); - } - - var offset = new DateTimeOffset(when); - - return this.CreateInstant(offset); - } - - /// - /// Converts an instant into a DateTime for the clock's time zone. - /// - /// The instant to convert. - /// A DateTime in the correct date and time. - public DateTime ToDateTime(Instant instant) - { - return instant - .InZone(this.DateTimeZone) - .ToDateTimeOffset() - .DateTime; - } - - /// - /// Sets the clock used for time calculations. - /// - /// The new clock to use. - /// The instance for chaining methods. - public NitrideClock WithClock(IClock clock) - { - this.Clock = clock; - return this; - } - - /// - /// Sets the date time zone and returns itself for chaining. - /// - /// The name of the time zone. - /// The instance for chaining methods. - public NitrideClock WithDateTimeZone(string timeZoneName) - { - this.DateTimeZone = DateTimeZoneProviders.Tzdb[timeZoneName]; - return this; - } - } -} diff --git a/src/Nitride.Temporal/NitrideTemporalBuilderExtensions.cs b/src/Nitride.Temporal/NitrideTemporalBuilderExtensions.cs index 08e4fd6..0385f9c 100644 --- a/src/Nitride.Temporal/NitrideTemporalBuilderExtensions.cs +++ b/src/Nitride.Temporal/NitrideTemporalBuilderExtensions.cs @@ -1,105 +1,91 @@ using System; + using Autofac; + using Nitride.Commands; +using Nitride.Temporal.Cli; + using Serilog; -using Expires = Nitride.Temporal.ExpiresPipelineCommandOption; -namespace Nitride.Temporal +using Expires = Nitride.Temporal.Cli.ExpiresPipelineCommandOption; + +namespace Nitride.Temporal; + +public static class NitrideTemporalBuilderExtensions { - public static class NitrideTemporalBuilderExtensions + /// + /// Extends the builder to allow for configuring the temporal + /// settings for generation. + /// + /// The host builder being configured. + /// The callback to configure the clock. + /// The builder passed in. + public static NitrideBuilder ConfigureClock(this NitrideBuilder builder, Action callback) { - /// - /// Extends the builder to allow for configuring the temporal - /// settings for generation. - /// - /// The host builder being configured. - /// The callback to configure the clock. - /// The builder passed in. - public static NitrideBuilder ConfigureClock( - this NitrideBuilder builder, - Action callback) - { - return builder - .UseTemporal() - .ConfigureSite( - (_, scope) => - { - var clock = scope.Resolve(); - callback(clock); - }); - } + return builder.UseTemporal() + .ConfigureSite( + (_, scope) => + { + Timekeeper? clock = scope.Resolve(); + callback(clock); + }); + } - public static NitrideBuilder UseTemporal(this NitrideBuilder builder) - { - return builder - .ConfigureContainer( - x => x.RegisterModule()); - } + public static NitrideBuilder UseTemporal(this NitrideBuilder builder) + { + return builder.ConfigureContainer(x => x.RegisterModule()); + } - /// - /// Adds the "--date=XXXX-XX-XX" option into the pipeline commands. - /// - /// The host builder being configured. - /// The builder passed in. - public static NitrideBuilder WithClockFromOptions( - this NitrideBuilder builder) - { - return builder - .UseTemporal() - .ConfigureContainer( - x => - { - x - .RegisterType() - .As(); - }); - } + /// + /// Adds the "--date=XXXX-XX-XX" option into the pipeline commands. + /// + /// The host builder being configured. + /// The builder passed in. + public static NitrideBuilder WithClockFromOptions(this NitrideBuilder builder) + { + return builder.UseTemporal() + .ConfigureContainer( + x => + { + x.RegisterType().As(); + }); + } - /// - /// Adds the "--expire=XXXX" option into the pipeline commands where - /// "XXX" is a format like "5y" or "500000:00:00.0". This is parsed by - /// TimeSpanParser which gives an easy format. - /// - /// The host builder being configured. - /// The default expiration, if one is provided. - /// The builder passed in. - public static NitrideBuilder WithExpiresFromOptions( - this NitrideBuilder builder, - TimeSpan defaultValue) - { - return WithExpiresFromOptions(builder, defaultValue.ToString()); - } + /// + /// Adds the "--expire=XXXX" option into the pipeline commands where + /// "XXX" is a format like "5y" or "500000:00:00.0". This is parsed by + /// TimeSpanParser which gives an easy format. + /// + /// The host builder being configured. + /// The default expiration, if one is provided. + /// The builder passed in. + public static NitrideBuilder WithExpiresFromOptions(this NitrideBuilder builder, TimeSpan defaultValue) + { + return WithExpiresFromOptions(builder, defaultValue.ToString()); + } - /// - /// Adds the "--expire=XXXX" option into the pipeline commands where - /// "XXX" is a format like "5y" or "500000:00:00.0". This is parsed by - /// TimeSpanParser which gives an easy format. - /// - /// The host builder being configured. - /// The default expiration, if one is provided. - /// The builder passed in. - public static NitrideBuilder WithExpiresFromOptions( - this NitrideBuilder builder, - string? defaultValue = null) - { - return builder - .ConfigureContainer( - x => - { - x - .Register( - context => - { - var logger = context.Resolve(); - var clock = context.Resolve(); + /// + /// Adds the "--expire=XXXX" option into the pipeline commands where + /// "XXX" is a format like "5y" or "500000:00:00.0". This is parsed by + /// TimeSpanParser which gives an easy format. + /// + /// The host builder being configured. + /// The default expiration, if one is provided. + /// The builder passed in. + public static NitrideBuilder WithExpiresFromOptions(this NitrideBuilder builder, string? defaultValue = null) + { + return builder.ConfigureContainer( + x => + { + x.Register( + context => + { + ILogger? logger = context.Resolve(); + Timekeeper? clock = context.Resolve(); - return new Expires( - logger, - clock, - defaultValue); - }) - .As(); - }); - } + return new Expires(logger, clock, defaultValue); + }) + .As(); + }); } } diff --git a/src/Nitride.Temporal/NitrideTemporalModule.cs b/src/Nitride.Temporal/NitrideTemporalModule.cs index 0443875..6fa02c8 100644 --- a/src/Nitride.Temporal/NitrideTemporalModule.cs +++ b/src/Nitride.Temporal/NitrideTemporalModule.cs @@ -1,15 +1,14 @@ using Autofac; -namespace Nitride.Temporal +namespace Nitride.Temporal; + +public class NitrideTemporalModule : Module { - public class NitrideTemporalModule : NitrideModuleBase + /// + protected override void Load(ContainerBuilder builder) { - /// - protected override void Register(ContainerBuilder builder) - { - builder - .RegisterGeneric(typeof(SetInstantFromComponent<>)) - .As(typeof(SetInstantFromComponent<>)); - } + builder.RegisterOperators(this); + builder.RegisterValidators(this); + builder.RegisterGeneric(typeof(SetInstantFromComponent<>)).As(typeof(SetInstantFromComponent<>)); } } diff --git a/src/Nitride.Temporal/README.md b/src/Nitride.Temporal/README.md index 67eb6d7..ee08cbc 100644 --- a/src/Nitride.Temporal/README.md +++ b/src/Nitride.Temporal/README.md @@ -39,4 +39,3 @@ NitrideBuilder builder; builder .ConfigureDates((NitrideClock clock) => clock.SetTimeZone()) ``` - diff --git a/src/Nitride.Temporal/SetInstantFromComponent.cs b/src/Nitride.Temporal/SetInstantFromComponent.cs index c9848d1..6227c1a 100644 --- a/src/Nitride.Temporal/SetInstantFromComponent.cs +++ b/src/Nitride.Temporal/SetInstantFromComponent.cs @@ -1,88 +1,90 @@ using System; using System.Collections.Generic; + +using FluentValidation; + using Gallium; + using NodaTime; -namespace Nitride.Temporal +namespace Nitride.Temporal; + +/// +/// Sets the instant from another component. This has methods for handling +/// nullable properties inside the component. +/// +public class SetInstantFromComponent : OperationBase { - /// - /// Sets the instant from another component. This has methods for handling - /// nullable properties inside the component. - /// - public class SetInstantFromComponent : NitrideOperationBase + private readonly Timekeeper clock; + + private readonly IValidator> validator; + + public SetInstantFromComponent(IValidator> validator, Timekeeper clock) { - private readonly NitrideClock clock; + this.validator = validator; + this.clock = clock; + } - public SetInstantFromComponent(NitrideClock clock) + /// + /// The callback used to get a date time object which is converted into + /// an Instant automatically. This can handle DateTime, DateTimeOffset, + /// LocalDate, and Instant. Nulls are skipped. + /// + public Func? GetDateTimeObject + { + get; + set; + } + + /// + public override IEnumerable Run(IEnumerable input) + { + this.validator.ValidateAndThrow(this); + + return input.ForEachEntity(this.Set); + } + + /// + /// Sets the callback used to get a date time object which is converted + /// into an Instant automatically. This can handle DateTime, + /// DateTimeOffset, LocalDate, and Instant. Nulls are skipped. + /// + public SetInstantFromComponent WithGetDateTimeObject(Func? callback) + { + this.GetDateTimeObject = callback; + return this; + } + + private Entity Set(Entity entity, TComponent component) + { + object? temporal = this.GetDateTimeObject!(entity, component); + Instant instant; + + switch (temporal) { - this.clock = clock; + case null: + return entity; + case Instant direct: + instant = direct; + break; + case LocalDate other: + instant = this.clock.CreateInstant(other); + break; + case DateTime other: + instant = this.clock.CreateInstant(other); + break; + case DateTimeOffset other: + instant = this.clock.CreateInstant(other); + break; + default: + throw new InvalidOperationException( + "Did not get a date time object from the callback. " + + "Can only handle DateTime, DateTimeOffset, LocalDate, " + + "and Instant. Got a " + + temporal.GetType().Name + + " instead."); } - /// - /// The callback used to get a date time object which is converted into - /// an Instant automatically. This can handle DateTime, DateTimeOffset, - /// LocalDate, and Instant. Nulls are skipped. - /// - public Func? GetDateTimeObject - { - get; - set; - } - - /// - public override IEnumerable Run(IEnumerable input) - { - this.CheckNotNull( - nameof(this.GetDateTimeObject), - this.GetDateTimeObject); - - return input - .ForEachEntity(this.Set); - } - - /// - /// Sets the callback used to get a date time object which is converted - /// into an Instant automatically. This can handle DateTime, - /// DateTimeOffset, LocalDate, and Instant. Nulls are skipped. - /// - public SetInstantFromComponent WithGetDateTimeObject( - Func? callback) - { - this.GetDateTimeObject = callback; - return this; - } - - private Entity Set(Entity entity, TComponent component) - { - object? temporal = this.GetDateTimeObject!(entity, component); - Instant instant; - - switch (temporal) - { - case null: - return entity; - case Instant direct: - instant = direct; - break; - case LocalDate other: - instant = this.clock.CreateInstant(other); - break; - case DateTime other: - instant = this.clock.CreateInstant(other); - break; - case DateTimeOffset other: - instant = this.clock.CreateInstant(other); - break; - default: - throw new InvalidOperationException( - "Did not get a date time object from the callback. " - + "Can only handle DateTime, DateTimeOffset, LocalDate, " - + "and Instant. Got a " - + temporal.GetType().Name - + " instead."); - } - - return entity.Set(instant); - } + return entity.Set(instant); } } diff --git a/src/Nitride.Temporal/SetInstantFromComponentValidator.cs b/src/Nitride.Temporal/SetInstantFromComponentValidator.cs new file mode 100644 index 0000000..9f3d0a0 --- /dev/null +++ b/src/Nitride.Temporal/SetInstantFromComponentValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace Nitride.Temporal; + +public class SetInstantFromComponentValidator : AbstractValidator> +{ + public SetInstantFromComponentValidator() + { + this.RuleFor(x => x.GetDateTimeObject).NotNull(); + } +} diff --git a/src/Nitride.Temporal/SetInstantFromPath.cs b/src/Nitride.Temporal/SetInstantFromPath.cs index 39cbc18..0240f5c 100644 --- a/src/Nitride.Temporal/SetInstantFromPath.cs +++ b/src/Nitride.Temporal/SetInstantFromPath.cs @@ -1,61 +1,67 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; + +using FluentValidation; + using Gallium; + using NodaTime; + using Zio; -namespace Nitride.Temporal +namespace Nitride.Temporal; + +/// +/// Sets the instant in the file based on the path of the entity. This +/// defaults to a generous regular expression that handles most formats of +/// four digit year, a separator, two digit month, a separator, and a two +/// digit day of month. +/// +[WithProperties] +public partial class SetInstantFromPath : OperationBase { - /// - /// Sets the instant in the file based on the path of the entity. This - /// defaults to a generous regular expression that handles most formats of - /// four digit year, a separator, two digit month, a separator, and a two - /// digit day of month. - /// - [WithProperties] - public partial class SetInstantFromPath : NitrideOperationBase + private readonly Timekeeper clock; + + private readonly IValidator validator; + + public SetInstantFromPath(IValidator validator, Timekeeper clock) { - private readonly NitrideClock clock; + this.validator = validator; + this.clock = clock; + this.PathRegex = new Regex(@"(?\d{4})[/-](?\d{2})[/-](?\d{2})"); + } - public SetInstantFromPath(NitrideClock clock) + /// + /// Gets or sets a regular expression that has three named expressions: + /// year, month, and day. + /// + public Regex? PathRegex { get; set; } + + /// + public override IEnumerable Run(IEnumerable input) + { + this.validator.ValidateAndThrow(this); + + return input.ForEachEntity(this.Set); + } + + private Entity Set(Entity entity, UPath path) + { + // See if the path matches the given expression. + Match match = this.PathRegex!.Match(path.ToString()); + + if (!match.Success) { - this.clock = clock; - this.PathRegex = new Regex( - @"(?\d{4})[/-](?\d{2})[/-](?\d{2})"); + return entity; } - /// - /// Gets or sets a regular expression that has three named expressions: - /// year, month, and day. - /// - public Regex? PathRegex { get; set; } + // Create an Instant from this. + Instant instant = this.clock.CreateInstant( + Convert.ToInt32(match.Groups["year"].Value), + Convert.ToInt32(match.Groups["month"].Value), + Convert.ToInt32(match.Groups["day"].Value)); - /// - public override IEnumerable Run(IEnumerable input) - { - this.CheckNotNull(nameof(this.PathRegex), this.PathRegex); - - return input.ForEachEntity(this.Set); - } - - private Entity Set(Entity entity, UPath path) - { - // See if the path matches the given expression. - Match match = this.PathRegex!.Match(path.ToString()); - - if (!match.Success) - { - return entity; - } - - // Create an Instant from this. - Instant instant = this.clock.CreateInstant( - Convert.ToInt32(match.Groups["year"].Value), - Convert.ToInt32(match.Groups["month"].Value), - Convert.ToInt32(match.Groups["day"].Value)); - - return entity.Set(instant); - } + return entity.Set(instant); } } diff --git a/src/Nitride.Temporal/SetInstantFromPathValidator.cs b/src/Nitride.Temporal/SetInstantFromPathValidator.cs new file mode 100644 index 0000000..f4cb184 --- /dev/null +++ b/src/Nitride.Temporal/SetInstantFromPathValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace Nitride.Temporal; + +public class SetInstantFromPathValidator : AbstractValidator +{ + public SetInstantFromPathValidator() + { + this.RuleFor(x => x.PathRegex).NotNull(); + } +} diff --git a/src/Nitride.Temporal/Timekeeper.cs b/src/Nitride.Temporal/Timekeeper.cs new file mode 100644 index 0000000..d570883 --- /dev/null +++ b/src/Nitride.Temporal/Timekeeper.cs @@ -0,0 +1,138 @@ +using System; + +using NodaTime; +using NodaTime.Testing; + +namespace Nitride.Temporal; + +/// +/// A class that handles date and time processing. This provides identifying +/// the desire time zone along with various methods for processing parsed +/// DateTime objects into NodaTime.Instant. +/// +public class Timekeeper +{ + public Timekeeper() + { + // We use FakeClock because we don't want time to advance in the + // middle of running, just in case we are just a few seconds before + // rollover. + Instant instant = SystemClock.Instance.GetCurrentInstant(); + + this.DateTimeZone = DateTimeZoneProviders.Bcl.GetSystemDefault(); + this.Clock = new FakeClock(instant); + } + + /// + /// Gets or sets the clock used to perform time calculations. + /// + public IClock Clock { get; set; } + + /// + /// Gets or sets the date time zone used for processing. + /// + public DateTimeZone DateTimeZone { get; set; } + + /// + /// Gets the instant when the files expire. + /// + public Instant? Expiration => this.Expires.HasValue + ? this.Clock.GetCurrentInstant().Minus(Duration.FromTimeSpan(this.Expires.Value)) + : null; + + /// + /// Gets or sets the expiration time span, which is calculated from the + /// current time. + /// + public TimeSpan? Expires { get; set; } + + /// + /// Creates an instant from a given year, month, and day. + /// + /// The numerical year. + /// The numerical month. + /// The numerical day of month. + /// An instant in the clock's time zone. + public Instant CreateInstant(int year, int month, int day) + { + var date = new LocalDate(year, month, day); + + return this.CreateInstant(date); + } + + /// + /// Creates an instant from the given local date. + /// + /// The date to parse. + /// An instant in the clock's time zone. + public Instant CreateInstant(LocalDate date) + { + ZonedDateTime zoned = date.AtStartOfDayInZone(this.DateTimeZone); + var instant = zoned.ToInstant(); + + return instant; + } + + /// + /// Creates an instant from the given date time zone. + /// + /// The object to convert. + /// An instant representing this DateTimeOffset. + public Instant CreateInstant(DateTimeOffset when) + { + var offset = OffsetDateTime.FromDateTimeOffset(when); + + return offset.ToInstant(); + } + + /// + /// Creates an instant from the given date time. + /// + /// The object to convert. + /// An instant representing this DateTimeOffset. + public Instant CreateInstant(DateTime when) + { + if (when.Kind == DateTimeKind.Unspecified) + { + var localDate = LocalDate.FromDateTime(when); + + return this.CreateInstant(localDate); + } + + var offset = new DateTimeOffset(when); + + return this.CreateInstant(offset); + } + + /// + /// Converts an instant into a DateTime for the clock's time zone. + /// + /// The instant to convert. + /// A DateTime in the correct date and time. + public DateTime ToDateTime(Instant instant) + { + return instant.InZone(this.DateTimeZone).ToDateTimeOffset().DateTime; + } + + /// + /// Sets the clock used for time calculations. + /// + /// The new clock to use. + /// The instance for chaining methods. + public Timekeeper WithClock(IClock clock) + { + this.Clock = clock; + return this; + } + + /// + /// Sets the date time zone and returns itself for chaining. + /// + /// The name of the time zone. + /// The instance for chaining methods. + public Timekeeper WithDateTimeZone(string timeZoneName) + { + this.DateTimeZone = DateTimeZoneProviders.Tzdb[timeZoneName]; + return this; + } +} diff --git a/src/Nitride.Yaml/HasYamlModel.cs b/src/Nitride.Yaml/HasYamlModel.cs new file mode 100644 index 0000000..ba50d16 --- /dev/null +++ b/src/Nitride.Yaml/HasYamlModel.cs @@ -0,0 +1,9 @@ +namespace Nitride.Yaml; + +/// +/// A marker class that indicates that the has a YAML model. +/// +public record HasYamlModel +{ + public static HasYamlModel Instance { get; } = new(); +} diff --git a/src/Nitride.Yaml/IsYaml.cs b/src/Nitride.Yaml/IsYaml.cs new file mode 100644 index 0000000..bd33eae --- /dev/null +++ b/src/Nitride.Yaml/IsYaml.cs @@ -0,0 +1,9 @@ +namespace Nitride.Yaml; + +/// +/// A marker class that indicates that the entity is YAML. +/// +public record IsYaml +{ + public static IsYaml Instance { get; } = new(); +} diff --git a/src/Nitride.Yaml/Nitride.Yaml.csproj b/src/Nitride.Yaml/Nitride.Yaml.csproj index 4221334..eb1754f 100644 --- a/src/Nitride.Yaml/Nitride.Yaml.csproj +++ b/src/Nitride.Yaml/Nitride.Yaml.csproj @@ -1,33 +1,33 @@ - - net5.0 - enable - + + net6.0 + enable + - - An extension to Nitride static site generator to parse YAML content. - + + An extension to Nitride static site generator to parse YAML content. + - - - + + + - - - - + + + + - - - True - + + + True + - - - Analyzer - False - - + + + Analyzer + False + + diff --git a/src/Nitride.Yaml/NitrideYamlBuilderExtensions.cs b/src/Nitride.Yaml/NitrideYamlBuilderExtensions.cs index 146b250..f713e60 100644 --- a/src/Nitride.Yaml/NitrideYamlBuilderExtensions.cs +++ b/src/Nitride.Yaml/NitrideYamlBuilderExtensions.cs @@ -1,14 +1,11 @@ using Autofac; -namespace Nitride.Yaml +namespace Nitride.Yaml; + +public static class NitrideYamlBuilderExtensions { - public static class NitrideYamlBuilderExtensions + public static NitrideBuilder UseYaml(this NitrideBuilder builder) { - public static NitrideBuilder UseYaml(this NitrideBuilder builder) - { - return builder - .ConfigureContainer( - x => x.RegisterModule()); - } + return builder.ConfigureContainer(x => x.RegisterModule()); } } diff --git a/src/Nitride.Yaml/NitrideYamlModule.cs b/src/Nitride.Yaml/NitrideYamlModule.cs index 09a5709..2052c95 100644 --- a/src/Nitride.Yaml/NitrideYamlModule.cs +++ b/src/Nitride.Yaml/NitrideYamlModule.cs @@ -1,15 +1,14 @@ using Autofac; -namespace Nitride.Yaml +namespace Nitride.Yaml; + +public class NitrideYamlModule : Module { - public class NitrideYamlModule : NitrideModuleBase + /// + protected override void Load(ContainerBuilder builder) { - /// - protected override void Register(ContainerBuilder builder) - { - builder - .RegisterGeneric(typeof(ParseYamlHeader<>)) - .As(typeof(ParseYamlHeader<>)); - } + builder.RegisterGeneric(typeof(ParseYamlHeader<>)).As(typeof(ParseYamlHeader<>)); + builder.RegisterOperators(this); + builder.RegisterValidators(this); } } diff --git a/src/Nitride.Yaml/ParseYamlHeader.cs b/src/Nitride.Yaml/ParseYamlHeader.cs index c4e6203..db635b1 100644 --- a/src/Nitride.Yaml/ParseYamlHeader.cs +++ b/src/Nitride.Yaml/ParseYamlHeader.cs @@ -1,163 +1,153 @@ using System.Collections.Generic; using System.IO; using System.Text; + using Gallium; + using Nitride.Contents; + using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; -namespace Nitride.Yaml +namespace Nitride.Yaml; + +/// +/// An operation that parses the header of a text file and pulls out the +/// YAML header with a specific type. +/// +/// The model that represents the header. +public class ParseYamlHeader : OperationBase + where TModel : class, new() { - /// - /// An operation that parses the header of a text file and pulls out the - /// YAML header with a specific type. - /// - /// The model that represents the header. - public class ParseYamlHeader : NitrideOperationBase - where TModel : class, new() + private bool ignoreUnmatchedProperties; + + private INamingConvention namingConvention; + + public ParseYamlHeader() { - private bool ignoreUnmatchedProperties; + this.namingConvention = CamelCaseNamingConvention.Instance; + this.ignoreUnmatchedProperties = true; + this.RemoveHeader = true; + } - private INamingConvention namingConvention; + /// + /// Gets or sets a value indicating whether the header should be removed + /// and the text content be removed. + /// + private bool RemoveHeader { get; set; } - public ParseYamlHeader() + /// + public override IEnumerable Run(IEnumerable input) + { + // Set up the YAML parsing. + DeserializerBuilder builder = new DeserializerBuilder().WithNamingConvention(this.namingConvention); + + if (this.ignoreUnmatchedProperties) { - this.namingConvention = CamelCaseNamingConvention.Instance; - this.ignoreUnmatchedProperties = true; - this.RemoveHeader = true; + builder = builder.IgnoreUnmatchedProperties(); } - /// - /// Gets or sets a value indicating whether the header should be removed - /// and the text content be removed. - /// - private bool RemoveHeader { get; set; } + IDeserializer deserializer = builder.Build(); - /// - public override IEnumerable Run(IEnumerable input) + // Process through the files. We only care about the text ones + // and we'll put a default TModel in for those that don't have a + // header. + return input.ForEachEntity((entity, content) => this.Parse(entity, content, deserializer)); + } + + /// + /// Sets the flag if the module should ignore unmatched properties + /// which defaults to true (ignore). + /// + /// The new value. + /// The module for chaining. + public ParseYamlHeader WithIgnoreUnmatchedProperties(bool value) + { + this.ignoreUnmatchedProperties = value; + return this; + } + + /// + /// Sets the naming convention to something other than the default + /// camel case. + /// + /// The new naming convention. + /// The module for chaining. + public ParseYamlHeader WithNamingConvention(INamingConvention value) + { + this.namingConvention = value; + return this; + } + + public ParseYamlHeader WithRemoveHeader(bool value) + { + this.RemoveHeader = value; + return this; + } + + private Entity Parse(Entity entity, ITextContent content, IDeserializer deserializer) + { + // Get the textual input from the stream. + using TextReader reader = content.GetReader(); + + // See if the first line is one that indicates a YAML header. + string? line = reader.ReadLine(); + + if (line != "---") { - // Set up the YAML parsing. - DeserializerBuilder builder = new DeserializerBuilder() - .WithNamingConvention(this.namingConvention); + // This file doesn't have a YAML header, so add the default + // version of our model and move on. + return entity.Set(new TModel()); + } - if (this.ignoreUnmatchedProperties) + // Read the rest of the header until we get to the end of the + // header. If we get to the end of the file first, then we don't + // have a valid file and just return the default. + StringBuilder buffer = new(); + + buffer.AppendLine(line); + + while ((line = reader.ReadLine()) != null) + { + // If have a separator, then we're done processing. + if (line == "---") { - builder = builder.IgnoreUnmatchedProperties(); + break; } - IDeserializer deserializer = builder.Build(); - - // Process through the files. We only care about the text ones - // and we'll put a default TModel in for those that don't have a - // header. - return input - .ForEachEntity( - (entity, content) => this.Parse( - entity, - content, - deserializer)); - } - - /// - /// Sets the flag if the module should ignore unmatched properties - /// which defaults to true (ignore). - /// - /// The new value. - /// The module for chaining. - public ParseYamlHeader WithIgnoreUnmatchedProperties(bool value) - { - this.ignoreUnmatchedProperties = value; - return this; - } - - /// - /// Sets the naming convention to something other than the default - /// camel case. - /// - /// The new naming convention. - /// The module for chaining. - public ParseYamlHeader WithNamingConvention( - INamingConvention value) - { - this.namingConvention = value; - return this; - } - - public ParseYamlHeader WithRemoveHeader(bool value) - { - this.RemoveHeader = value; - return this; - } - - private Entity Parse( - Entity entity, - ITextContent content, - IDeserializer deserializer) - { - // Get the textual input from the stream. - using TextReader reader = content.GetReader(); - - // See if the first line is one that indicates a YAML header. - string? line = reader.ReadLine(); - - if (line != "---") - { - // This file doesn't have a YAML header, so add the default - // version of our model and move on. - return entity.Set(new TModel()); - } - - // Read the rest of the header until we get to the end of the - // header. If we get to the end of the file first, then we don't - // have a valid file and just return the default. - StringBuilder buffer = new(); - + // Read the next line into memory. buffer.AppendLine(line); - - while ((line = reader.ReadLine()) != null) - { - // If have a separator, then we're done processing. - if (line == "---") - { - break; - } - - // Read the next line into memory. - buffer.AppendLine(line); - } - - // If the line is null, then we got to the end of the file without - // finding a proper header, so use a default and have no other - // changes. - if (line == null) - { - return entity.Set(new TModel()); - } - - // Pull out the model so we can append it later. - string yaml = buffer.ToString(); - - var model = deserializer.Deserialize(yaml); - - // If we are not removing the header, then we're done. - if (!this.RemoveHeader) - { - return entity.Set(model); - } - - // Read the rest of the reader into a new (reused) buffer so we can - // set the new text content to the text without the header. - buffer.Length = 0; - - while ((line = reader.ReadLine()) != null) - { - buffer.AppendLine(line); - } - - // Set the model and return it. - return entity - .Set(model) - .SetTextContent(new StringTextContent(buffer.ToString())); } + + // If the line is null, then we got to the end of the file without + // finding a proper header, so use a default and have no other + // changes. + if (line == null) + { + return entity.Set(new TModel()); + } + + // Pull out the model so we can append it later. + string yaml = buffer.ToString(); + + TModel? model = deserializer.Deserialize(yaml); + + // If we are not removing the header, then we're done. + if (!this.RemoveHeader) + { + return entity.Set(model); + } + + // Read the rest of the reader into a new (reused) buffer so we can + // set the new text content to the text without the header. + buffer.Length = 0; + + while ((line = reader.ReadLine()) != null) + { + buffer.AppendLine(line); + } + + // Set the model and return it. + return entity.Set(model).SetTextContent(new StringTextContent(buffer.ToString())).Set(HasYamlModel.Instance); } } diff --git a/src/Nitride/Commands/BuildCommand.cs b/src/Nitride/Commands/BuildCommand.cs index 9444b27..ac215b1 100644 --- a/src/Nitride/Commands/BuildCommand.cs +++ b/src/Nitride/Commands/BuildCommand.cs @@ -2,55 +2,58 @@ using System.Collections.Generic; using System.CommandLine; using System.CommandLine.Invocation; using System.Threading.Tasks; + using Nitride.Pipelines; + using Serilog; -namespace Nitride.Commands +namespace Nitride.Commands; + +/// +/// The basic command to generate a website and run through the pipelines. +/// +public class BuildCommand : Command, ICommandHandler { - /// - /// The basic command to generate a website and run through the pipelines. - /// - public class BuildCommand : Command, ICommandHandler, ITopLevelCommand + private readonly ILogger logger; + + private readonly IList pipelineOptions; + + private readonly PipelineManager pipelines; + + public BuildCommand(ILogger logger, PipelineManager pipelines, IList pipelineOptions) + : base("build", "Generate the website") { - private readonly ILogger logger; + // Set up our simple member variables. + this.pipelines = pipelines; + this.pipelineOptions = pipelineOptions; + this.logger = logger.ForContext(); + this.Handler = this; - private readonly IList pipelineOptions; - - private readonly PipelineManager pipelines; - - public BuildCommand( - ILogger logger, - PipelineManager pipelines, - IList pipelineOptions) - : base("build", "Generate the website") + // Handle any injected arguments into the command line. + foreach (IPipelineCommandOption? option in pipelineOptions) { - // Set up our simple member variables. - this.pipelines = pipelines; - this.pipelineOptions = pipelineOptions; - this.logger = logger.ForContext(); - this.Handler = this; - - // Handle any injected arguments into the command line. - foreach (var option in pipelineOptions) - { - this.AddOption(option.Option); - } - } - - /// - public async Task InvokeAsync(InvocationContext context) - { - // Process any injected options. - foreach (var option in this.pipelineOptions) - { - option.Handle(context); - } - - // This is the main entry point into the `build` command. This runs - // all the pipelines once and then quits when it finishes. - int pipelinesResults = await this.pipelines.RunAsync(); - - return pipelinesResults; + this.AddOption(option.Option); } } + + /// + public async Task InvokeAsync(InvocationContext context) + { + // Process any injected options. + this.logger.Debug("Processing {Count:N0} pipeline options", this.pipelineOptions.Count); + + foreach (IPipelineCommandOption? option in this.pipelineOptions) + { + this.logger.Verbose("Processing pipeline option: {Option}", option); + option.Handle(context); + } + + // This is the main entry point into the `build` command. This runs + // all the pipelines once and then quits when it finishes. + this.logger.Information("Running pipelines"); + + int pipelinesResults = await this.pipelines.RunAsync(); + + return pipelinesResults; + } } diff --git a/src/Nitride/Commands/IPipelineCommandOption.cs b/src/Nitride/Commands/IPipelineCommandOption.cs index 6dbc6df..4803268 100644 --- a/src/Nitride/Commands/IPipelineCommandOption.cs +++ b/src/Nitride/Commands/IPipelineCommandOption.cs @@ -1,23 +1,22 @@ using System.CommandLine; using System.CommandLine.Invocation; -namespace Nitride.Commands +namespace Nitride.Commands; + +/// +/// Creates new options for the build command to allow injecting an +/// option into the build command. +/// +public interface IPipelineCommandOption { /// - /// Creates new options for the build command to allow injecting an - /// option into the build command. + /// The option that used to bind to the command. /// - public interface IPipelineCommandOption - { - /// - /// The option that used to bind to the command. - /// - Option Option { get; } + Option Option { get; } - /// - /// Handles the command after it has been created and the options bound. - /// - /// The context of the CLI command. - void Handle(InvocationContext context); - } + /// + /// Handles the command after it has been created and the options bound. + /// + /// The context of the CLI command. + void Handle(InvocationContext context); } diff --git a/src/Nitride/Commands/ITopLevelCommand.cs b/src/Nitride/Commands/ITopLevelCommand.cs deleted file mode 100644 index 301a6b8..0000000 --- a/src/Nitride/Commands/ITopLevelCommand.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.CommandLine; - -namespace Nitride.Commands -{ - /// - /// An interface that indicates that the given command is a top-level - /// command instead of one that is included as a sub-command inside another. - /// - public interface ITopLevelCommand : ICommand - { - } -} diff --git a/src/Nitride/ContainerBuiltHandler.cs b/src/Nitride/ContainerBuiltHandler.cs index a851e7e..1da9114 100644 --- a/src/Nitride/ContainerBuiltHandler.cs +++ b/src/Nitride/ContainerBuiltHandler.cs @@ -1,7 +1,6 @@ -namespace Nitride -{ - /// - /// Describes the signature of a callback for after the container is build. - /// - public delegate void ContainerBuiltHandler(); -} +namespace Nitride; + +/// +/// Describes the signature of a callback for after the container is build. +/// +public delegate void ContainerBuiltHandler(); diff --git a/src/Nitride/Contents/ByteArrayBinaryContent.cs b/src/Nitride/Contents/ByteArrayBinaryContent.cs index f8430d6..1ff4766 100644 --- a/src/Nitride/Contents/ByteArrayBinaryContent.cs +++ b/src/Nitride/Contents/ByteArrayBinaryContent.cs @@ -1,26 +1,24 @@ using System; using System.IO; -namespace Nitride.Contents +namespace Nitride.Contents; + +/// +/// Implements a purely in-memory binary content provider. +/// +public class ByteArrayBinaryContent : IBinaryContent { - /// - /// Implements a purely in-memory binary content provider. - /// - public class ByteArrayBinaryContent : IBinaryContent + private readonly byte[] buffer; + + public ByteArrayBinaryContent(byte[] buffer) { - private readonly byte[] buffer; + this.buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); + } - public ByteArrayBinaryContent(byte[] buffer) - { - this.buffer = - buffer ?? throw new ArgumentNullException(nameof(buffer)); - } - - /// - public Stream GetStream() - { - // Return a memory stream that cannot be written. - return new MemoryStream(this.buffer, false); - } + /// + public Stream GetStream() + { + // Return a memory stream that cannot be written. + return new MemoryStream(this.buffer, false); } } diff --git a/src/Nitride/Contents/EntityBinaryContentExtensions.cs b/src/Nitride/Contents/EntityBinaryContentExtensions.cs index 954c65a..c4e2849 100644 --- a/src/Nitride/Contents/EntityBinaryContentExtensions.cs +++ b/src/Nitride/Contents/EntityBinaryContentExtensions.cs @@ -1,47 +1,44 @@ using Gallium; -namespace Nitride.Contents +namespace Nitride.Contents; + +/// +/// Various extension methods for working with binary content. +/// +public static class EntityBinaryContentExtensions { /// - /// Various extension methods for working with binary content. + /// Retrieves the binary content associated with this entity. /// - public static class EntityBinaryContentExtensions + /// The entity to query. + /// The binary content. + public static IBinaryContent GetBinaryContent(this Entity entity) { - /// - /// Retrieves the binary content associated with this entity. - /// - /// The entity to query. - /// The binary content. - public static IBinaryContent GetBinaryContent(this Entity entity) - { - return entity.Get(); - } + return entity.Get(); + } - /// - /// Determines if the entity has a registered IBinaryContent component. - /// - /// The entity to inspect. - /// True if there is a component extending `IContent`. - public static bool HasBinaryContent(this Entity entity) - { - return entity.Has(); - } + /// + /// Determines if the entity has a registered IBinaryContent component. + /// + /// The entity to inspect. + /// True if there is a component extending `IContent`. + public static bool HasBinaryContent(this Entity entity) + { + return entity.Has(); + } - /// - /// Remove all existing content components from the entity and then adds - /// the new binary content as a component into the entity before - /// returning it. - /// - /// The entity to query and modify. - /// The content to add to the entity. - /// The base type of the content. - /// The entity or a copy with the new component. - public static Entity SetBinaryContent( - this Entity entity, - TType content) - where TType : IBinaryContent - { - return entity.SetContent(content); - } + /// + /// Remove all existing content components from the entity and then adds + /// the new binary content as a component into the entity before + /// returning it. + /// + /// The entity to query and modify. + /// The content to add to the entity. + /// The base type of the content. + /// The entity or a copy with the new component. + public static Entity SetBinaryContent(this Entity entity, TType content) + where TType : IBinaryContent + { + return entity.SetContent(content); } } diff --git a/src/Nitride/Contents/EntityContentExtensions.cs b/src/Nitride/Contents/EntityContentExtensions.cs index 1aa8ad5..7ea02e4 100644 --- a/src/Nitride/Contents/EntityContentExtensions.cs +++ b/src/Nitride/Contents/EntityContentExtensions.cs @@ -1,65 +1,57 @@ using System; using System.Collections.Generic; using System.Linq; + using Gallium; -namespace Nitride.Contents +namespace Nitride.Contents; + +/// +/// Various extension methods for working with content. +/// +public static class EntityContentExtensions { /// - /// Various extension methods for working with content. + /// Retrieves the single registered content entity inside the component. + /// If there is more than one, this will throw an exception. /// - public static class EntityContentExtensions + /// The entity to query. + /// The one and only content component. + public static IContent GetContent(this Entity entity) { - /// - /// Retrieves the single registered content entity inside the component. - /// If there is more than one, this will throw an exception. - /// - /// The entity to query. - /// The one and only content component. - public static IContent GetContent(this Entity entity) - { - Type type = entity - .GetComponentTypes() - .Single(x => typeof(IContent).IsAssignableFrom(x)); + Type type = entity.GetComponentTypes().Single(x => typeof(IContent).IsAssignableFrom(x)); - return entity.Get(type); + return entity.Get(type); + } + + /// + /// Determines if the entity has at least one content component. + /// + /// The entity to inspect. + /// True if there is a component extending `IContent`. + public static bool HasContent(this Entity entity) + { + return entity.GetComponentTypes().Any(x => typeof(IContent).IsAssignableFrom(x)); + } + + /// + /// Remove all existing content components from the entity and then adds + /// the new component into the entity before returning it. + /// + /// The entity to query and modify. + /// The content to add to the entity. + /// The base type of the content. + /// The entity or a copy with the new component. + public static Entity SetContent(this Entity entity, TType content) + where TType : IContent + { + IEnumerable? existing = entity.GetComponentTypes().Where(x => typeof(IContent).IsAssignableFrom(x)); + + foreach (Type? component in existing) + { + entity = entity.Remove(component); } - /// - /// Determines if the entity has at least one content component. - /// - /// The entity to inspect. - /// True if there is a component extending `IContent`. - public static bool HasContent(this Entity entity) - { - return entity - .GetComponentTypes() - .Any(x => typeof(IContent).IsAssignableFrom(x)); - } - - /// - /// Remove all existing content components from the entity and then adds - /// the new component into the entity before returning it. - /// - /// The entity to query and modify. - /// The content to add to the entity. - /// The base type of the content. - /// The entity or a copy with the new component. - public static Entity SetContent( - this Entity entity, - TType content) - where TType : IContent - { - IEnumerable? existing = entity - .GetComponentTypes() - .Where(x => typeof(IContent).IsAssignableFrom(x)); - - foreach (var component in existing) - { - entity = entity.Remove(component); - } - - return entity.Add(content); - } + return entity.Add(content); } } diff --git a/src/Nitride/Contents/EntityTextContentExtensions.cs b/src/Nitride/Contents/EntityTextContentExtensions.cs index 71f479a..4b2752e 100644 --- a/src/Nitride/Contents/EntityTextContentExtensions.cs +++ b/src/Nitride/Contents/EntityTextContentExtensions.cs @@ -1,53 +1,48 @@ using Gallium; -namespace Nitride.Contents +namespace Nitride.Contents; + +/// +/// Various extension methods for working with text content. +/// +public static class EntityTextContentExtensions { /// - /// Various extension methods for working with text content. + /// Determines if the entity has a registered ITextContent component. /// - public static class EntityTextContentExtensions + /// The entity to inspect. + /// True if there is a component extending `IContent`. + public static bool HasTextContent(this Entity entity) { - /// - /// Determines if the entity has a registered ITextContent component. - /// - /// The entity to inspect. - /// True if there is a component extending `IContent`. - public static bool HasTextContent(this Entity entity) - { - return entity.Has(); - } + return entity.Has(); + } - /// - /// Remove all existing content components from the entity and then adds - /// the new text content as a component into the entity before - /// returning it. - /// - /// The entity to query and modify. - /// The content to add to the entity. - /// The base type of the content. - /// The entity or a copy with the new component. - public static Entity SetTextContent( - this Entity entity, - TType content) - where TType : ITextContent - { - return entity.SetContent(content); - } + /// + /// Remove all existing content components from the entity and then adds + /// the new text content as a component into the entity before + /// returning it. + /// + /// The entity to query and modify. + /// The content to add to the entity. + /// The base type of the content. + /// The entity or a copy with the new component. + public static Entity SetTextContent(this Entity entity, TType content) + where TType : ITextContent + { + return entity.SetContent(content); + } - /// - /// Remove all existing content components from the entity and then adds - /// the new text content as a component into the entity before - /// returning it. - /// - /// The entity to query and modify. - /// The content to add to the entity. - /// The base type of the content. - /// The entity or a copy with the new component. - public static Entity SetTextContent( - this Entity entity, - string content) - { - return entity.SetTextContent(new StringTextContent(content)); - } + /// + /// Remove all existing content components from the entity and then adds + /// the new text content as a component into the entity before + /// returning it. + /// + /// The entity to query and modify. + /// The content to add to the entity. + /// The base type of the content. + /// The entity or a copy with the new component. + public static Entity SetTextContent(this Entity entity, string content) + { + return entity.SetTextContent(new StringTextContent(content)); } } diff --git a/src/Nitride/Contents/FileBinaryContent.cs b/src/Nitride/Contents/FileBinaryContent.cs index 8e20643..14dfeec 100644 --- a/src/Nitride/Contents/FileBinaryContent.cs +++ b/src/Nitride/Contents/FileBinaryContent.cs @@ -1,44 +1,39 @@ using System; using System.IO; -namespace Nitride.Contents +namespace Nitride.Contents; + +/// +/// A binary content provider based on a file in the file system. +/// +public class FileBinaryContent : IBinaryContent, ITextContentConvertable { - /// - /// A binary content provider based on a file in the file system. - /// - public class FileBinaryContent : IBinaryContent, ITextContentConvertable + private readonly string path; + + public FileBinaryContent(string path) { - private readonly string path; + this.path = path ?? throw new ArgumentNullException(nameof(path)); + } - public FileBinaryContent(string path) + public FileBinaryContent(FileInfo file) + { + if (file == null) { - this.path = path ?? throw new ArgumentNullException(nameof(path)); + throw new ArgumentNullException(nameof(file)); } - public FileBinaryContent(FileInfo file) - { - if (file == null) - { - throw new ArgumentNullException(nameof(file)); - } + this.path = file.FullName; + } - this.path = file.FullName; - } + /// + public Stream GetStream() + { + return File.Open(this.path, FileMode.Open, FileAccess.Read, FileShare.Read); + } - /// - public Stream GetStream() - { - return File.Open( - this.path, - FileMode.Open, - FileAccess.Read, - FileShare.Read); - } - - /// - public ITextContent ToTextContent() - { - return new FileTextContent(this.path); - } + /// + public ITextContent ToTextContent() + { + return new FileTextContent(this.path); } } diff --git a/src/Nitride/Contents/FileTextContent.cs b/src/Nitride/Contents/FileTextContent.cs index 497123d..9f27f10 100644 --- a/src/Nitride/Contents/FileTextContent.cs +++ b/src/Nitride/Contents/FileTextContent.cs @@ -2,46 +2,39 @@ using System; using System.IO; using System.Text; -namespace Nitride.Contents +namespace Nitride.Contents; + +/// +/// A text content provider based on a file in the file system. +/// +public class FileTextContent : ITextContent, IBinaryContentConvertable { - /// - /// A text content provider based on a file in the file system. - /// - public class FileTextContent : ITextContent, IBinaryContentConvertable + private readonly string path; + + public FileTextContent(string path) { - private readonly string path; + this.path = path ?? throw new ArgumentNullException(nameof(path)); + } - public FileTextContent(string path) + public FileTextContent(FileInfo file) + { + if (file == null) { - this.path = path ?? throw new ArgumentNullException(nameof(path)); + throw new ArgumentNullException(nameof(file)); } - public FileTextContent(FileInfo file) - { - if (file == null) - { - throw new ArgumentNullException(nameof(file)); - } + this.path = file.FullName; + } - this.path = file.FullName; - } + /// + public TextReader GetReader() + { + return new StreamReader(File.Open(this.path, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8); + } - /// - public TextReader GetReader() - { - return new StreamReader( - File.Open( - this.path, - FileMode.Open, - FileAccess.Read, - FileShare.Read), - Encoding.UTF8); - } - - /// - public IBinaryContent ToBinaryContent() - { - return new FileBinaryContent(this.path); - } + /// + public IBinaryContent ToBinaryContent() + { + return new FileBinaryContent(this.path); } } diff --git a/src/Nitride/Contents/IBinaryContent.cs b/src/Nitride/Contents/IBinaryContent.cs index f7c0147..b173d12 100644 --- a/src/Nitride/Contents/IBinaryContent.cs +++ b/src/Nitride/Contents/IBinaryContent.cs @@ -1,19 +1,18 @@ using System.IO; -namespace Nitride.Contents +namespace Nitride.Contents; + +/// +/// Indicates a content source that works with binary data and streams. +/// +public interface IBinaryContent : IContent { /// - /// Indicates a content source that works with binary data and streams. + /// Opens a read-only stream into the underlying source of binary data. /// - public interface IBinaryContent : IContent - { - /// - /// Opens a read-only stream into the underlying source of binary data. - /// - /// - /// It is up to the calling class to close this stream. - /// - /// A stream to the internal data. - Stream GetStream(); - } + /// + /// It is up to the calling class to close this stream. + /// + /// A stream to the internal data. + Stream GetStream(); } diff --git a/src/Nitride/Contents/IBinaryContentConvertable.cs b/src/Nitride/Contents/IBinaryContentConvertable.cs index 525cff9..6035e3d 100644 --- a/src/Nitride/Contents/IBinaryContentConvertable.cs +++ b/src/Nitride/Contents/IBinaryContentConvertable.cs @@ -1,15 +1,14 @@ -namespace Nitride.Contents +namespace Nitride.Contents; + +/// +/// An interface to indicates an object can be converted into a binary +/// content directly. +/// +public interface IBinaryContentConvertable { /// - /// An interface to indicates an object can be converted into a binary - /// content directly. + /// Convert the given object into a binary content. /// - public interface IBinaryContentConvertable - { - /// - /// Convert the given object into a binary content. - /// - /// - IBinaryContent ToBinaryContent(); - } + /// + IBinaryContent ToBinaryContent(); } diff --git a/src/Nitride/Contents/IContent.cs b/src/Nitride/Contents/IContent.cs index 4035fd9..7e52911 100644 --- a/src/Nitride/Contents/IContent.cs +++ b/src/Nitride/Contents/IContent.cs @@ -1,12 +1,11 @@ -namespace Nitride.Contents +namespace Nitride.Contents; + +/// +/// Describes a content provider or source for an Entity. `IContent` is +/// also used for the mutual exclusivity login in `EntityContentExtensions` +/// where all `IContent` derived components are automatically removed when +/// setting a new content source. +/// +public interface IContent { - /// - /// Describes a content provider or source for an Entity. `IContent` is - /// also used for the mutual exclusivity login in `EntityContentExtensions` - /// where all `IContent` derived components are automatically removed when - /// setting a new content source. - /// - public interface IContent - { - } } diff --git a/src/Nitride/Contents/ITextContent.cs b/src/Nitride/Contents/ITextContent.cs index 9a79e70..5af1707 100644 --- a/src/Nitride/Contents/ITextContent.cs +++ b/src/Nitride/Contents/ITextContent.cs @@ -1,19 +1,18 @@ using System.IO; -namespace Nitride.Contents +namespace Nitride.Contents; + +/// +/// Indicates a content source that provides data as a TextReader and text. +/// +public interface ITextContent : IContent { /// - /// Indicates a content source that provides data as a TextReader and text. + /// Opens a text reader into the underlying source of text data. /// - public interface ITextContent : IContent - { - /// - /// Opens a text reader into the underlying source of text data. - /// - /// - /// It is up to the calling class to close this reader. - /// - /// The text reader to the internal data. - TextReader GetReader(); - } + /// + /// It is up to the calling class to close this reader. + /// + /// The text reader to the internal data. + TextReader GetReader(); } diff --git a/src/Nitride/Contents/ITextContentConvertable.cs b/src/Nitride/Contents/ITextContentConvertable.cs index e7f1fad..e380c9d 100644 --- a/src/Nitride/Contents/ITextContentConvertable.cs +++ b/src/Nitride/Contents/ITextContentConvertable.cs @@ -1,15 +1,14 @@ -namespace Nitride.Contents +namespace Nitride.Contents; + +/// +/// An interface to indicates an object can be converted into a text +/// content directly. +/// +public interface ITextContentConvertable { /// - /// An interface to indicates an object can be converted into a text - /// content directly. + /// Convert the given object into a text content. /// - public interface ITextContentConvertable - { - /// - /// Convert the given object into a text content. - /// - /// - ITextContent ToTextContent(); - } + /// + ITextContent ToTextContent(); } diff --git a/src/Nitride/Contents/README.md b/src/Nitride/Contents/README.md index fe29826..e435417 100644 --- a/src/Nitride/Contents/README.md +++ b/src/Nitride/Contents/README.md @@ -7,7 +7,7 @@ as `IBinaryContent` and `ITextContent`. ## Exclusivity -The content interfaces are treated as *mutually exclusive* with each other by +The content interfaces are treated as _mutually exclusive_ with each other by extending the `IContent` interface. The intended reason for this is to have a single source of data that doesn't go out of sync. Adding or setting a `IBinaryContent` will remove the other content components in the entity, even @@ -133,7 +133,7 @@ new FileBinaryContent(file); ## ITextContent -Text content allows for retrieving the contents via `TextReader` instead of a +Text content allows for retrieving the contents via `TextReader` instead of a stream. ```c# diff --git a/src/Nitride/Contents/StringTextContent.cs b/src/Nitride/Contents/StringTextContent.cs index 770ea0c..836cd35 100644 --- a/src/Nitride/Contents/StringTextContent.cs +++ b/src/Nitride/Contents/StringTextContent.cs @@ -2,36 +2,34 @@ using System; using System.IO; using System.Text; -namespace Nitride.Contents +namespace Nitride.Contents; + +/// +/// Implements a pure, in-memory text content provider that uses a string +/// as the backing store. +/// +public class StringTextContent : ITextContent { - /// - /// Implements a pure, in-memory text content provider that uses a string - /// as the backing store. - /// - public class StringTextContent : ITextContent + private readonly string buffer; + + public StringTextContent(StringBuilder buffer) + : this(buffer.ToString()) { - private readonly string buffer; + } - public StringTextContent(StringBuilder buffer) - : this(buffer.ToString()) - { - } + public StringTextContent(string buffer) + { + this.buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); + } - public StringTextContent(string buffer) - { - this.buffer = - buffer ?? throw new ArgumentNullException(nameof(buffer)); - } + public StringTextContent(params string[] lines) + : this(string.Join("\n", lines)) + { + } - public StringTextContent(params string[] lines) - : this(string.Join("\n", lines)) - { - } - - /// - public TextReader GetReader() - { - return new StringReader(this.buffer); - } + /// + public TextReader GetReader() + { + return new StringReader(this.buffer); } } diff --git a/src/Nitride/Contents/TextContentExtensions.cs b/src/Nitride/Contents/TextContentExtensions.cs index 133c52e..38ecb5f 100644 --- a/src/Nitride/Contents/TextContentExtensions.cs +++ b/src/Nitride/Contents/TextContentExtensions.cs @@ -1,22 +1,21 @@ using System.IO; -namespace Nitride.Contents +namespace Nitride.Contents; + +public static class TextContentExtensions { - public static class TextContentExtensions + public static string GetText(this ITextContent content) { - public static string GetText(this ITextContent content) + var writer = new StringWriter(); + using TextReader reader = content.GetReader(); + string? line = reader.ReadLine(); + + while (line != null) { - var writer = new StringWriter(); - using TextReader reader = content.GetReader(); - string? line = reader.ReadLine(); - - while (line != null) - { - writer.WriteLine(line); - line = reader.ReadLine(); - } - - return writer.ToString(); + writer.WriteLine(line); + line = reader.ReadLine(); } + + return writer.ToString(); } } diff --git a/src/Nitride/Entities/CreateIndexEntities.cs b/src/Nitride/Entities/CreateIndexEntities.cs index 61dd6c2..0fd16af 100644 --- a/src/Nitride/Entities/CreateIndexEntities.cs +++ b/src/Nitride/Entities/CreateIndexEntities.cs @@ -1,181 +1,178 @@ using System; using System.Collections.Generic; + +using FluentValidation; + using Gallium; + using Serilog; -namespace Nitride.Entities +namespace Nitride.Entities; + +/// +/// A Nitride operation that creates and merges entities that are intended +/// to be indexes of another entity. For example, this could be year and +/// month archive pages, tag or category pages. Support is given for +/// merging existing pages so a description could be written from a file +/// and then the index logic is automatically added. +/// +public class CreateIndexEntities : OperationBase + where TIndexKey : notnull { - /// - /// A Nitride operation that creates and merges entities that are intended - /// to be indexes of another entity. For example, this could be year and - /// month archive pages, tag or category pages. Support is given for - /// merging existing pages so a description could be written from a file - /// and then the index logic is automatically added. - /// - public partial class CreateIndexEntities : NitrideOperationBase - where TIndexKey : notnull + // TODO: This does not use [WithProperties] because the source generator hasn't been taught how to do generics. + + private readonly ILogger logger; + + private readonly IValidator> validator; + + public CreateIndexEntities(ILogger logger, IValidator> validator) { - private readonly ILogger logger; + this.validator = validator; + this.logger = logger.ForContext(typeof(CreateIndexEntities<>)); + } - public CreateIndexEntities(ILogger logger) + /// + /// Creates an index for a given key. This will not be called for any + /// index that has been already created. + /// + public Func, Entity>? CreateIndexEntity + { + get; + set; + } + + /// + /// Gets or sets the function to retrieve the key from an existing + /// index page. If this returns null, then the entity is considered not + /// to be an index page. + /// + public Func? GetIndexEntityKey { get; set; } + + /// + /// A method that gets the keys for a given entity. If this returns an + /// empty list, then the entity will not added to an index. + /// + public Func>? GetIndexKeys { get; set; } + + /// + /// Updates an existing index entity to include new information. + /// + public Func, Entity>? UpdateIndexEntity + { + get; + set; + } + + /// + public override IEnumerable Run(IEnumerable input) + { + // Make sure we have sane data. + this.validator.ValidateAndThrow(this); + + // We need to process two lists out of the output, so we need to put + // it into a list so we can enumerate through it twice. This will + // also cause the output to be reordered. + Dictionary indexes = new(); + Dictionary> indexed = new(); + List results = new(); + + foreach (Entity? entity in input) { - this.logger = logger.ForContext(typeof(CreateIndexEntities<>)); - } - - /// - /// Creates an index for a given key. This will not be called for any - /// index that has been already created. - /// - public Func, Entity>? CreateIndexEntity - { - get; - set; - } - - /// - /// Gets or sets the function to retrieve the key from an existing - /// index page. If this returns null, then the entity is considered not - /// to be an index page. - /// - public Func? GetIndexEntityKey { get; set; } - - /// - /// A method that gets the keys for a given entity. If this returns an - /// empty list, then the entity will not added to an index. - /// - public Func>? GetIndexKeys { get; set; } - - /// - /// Updates an existing index entity to include new information. - /// - public Func, Entity>? UpdateIndexEntity - { - get; - set; - } - - /// - public override IEnumerable Run(IEnumerable input) - { - // Make sure we have sane data. - this.CheckNotNull( - nameof(this.CreateIndexEntity), - this.CreateIndexEntity); - this.CheckNotNull( - nameof(this.GetIndexKeys), - this.GetIndexKeys); - this.CheckNotNull( - nameof(this.UpdateIndexEntity), - this.UpdateIndexEntity); - - // We need to process two lists out of the output, so we need to put - // it into a list so we can enumerate through it twice. This will - // also cause the output to be reordered. - Dictionary indexes = new(); - Dictionary> indexed = new(); - List results = new(); - - foreach (var entity in input) + // See if we are an index page first. + if (this.GetIndexEntityKey != null) { - // See if we are an index page first. - if (this.GetIndexEntityKey != null) - { - TIndexKey? indexKey = this.GetIndexEntityKey(entity); - - if (indexKey != null) - { - indexes[indexKey] = entity; - continue; - } - } - - // We aren't an index, so check to see if this entity is - // something to be indexed. - foreach (TIndexKey indexedKey in this.GetIndexKeys!(entity)) - { - if (!indexed.TryGetValue(indexedKey, out var list)) - { - indexed[indexedKey] = list = new List(); - } - - list.Add(entity); - } - - // Add to the non-index page list. - results.Add(entity); - } - - // Go through all the index pages and update them. We get a list of - // all the pages in the index and pass them into the function to - // update the existing index. Then we update the entity and add it - // to the bottom of the results list. - foreach ((TIndexKey key, var oldIndex) in indexes) - { - if (!indexed.TryGetValue(key, out var list)) - { - list = new List(); - } - - Entity newEntity = this.UpdateIndexEntity!(oldIndex, key, list); - - results.Add(newEntity); - } - - // Go through all the known index keys and create the missing pages. - int created = 0; - - foreach ((TIndexKey key, var list) in indexed) - { - // See if we already have a page, if we do, then we've already - // processed that page and don't have to do anything. - if (indexes.ContainsKey(key)) + TIndexKey? indexKey = this.GetIndexEntityKey(entity); + + if (indexKey != null) { + indexes[indexKey] = entity; continue; } - - // We don't have that page and need to add it to the list. - Entity entity = this.CreateIndexEntity!(key, list); - - created++; - results.Add(entity); } - // Return the combined together version. - this.logger.Debug( - "Found {Old:N0} and created {New:N0} index pages for {Keys:N0} keys", - indexes.Count, - created, - indexed.Count); + // We aren't an index, so check to see if this entity is + // something to be indexed. + foreach (TIndexKey indexedKey in this.GetIndexKeys!(entity)) + { + if (!indexed.TryGetValue(indexedKey, out List? list)) + { + indexed[indexedKey] = list = new List(); + } - return results; + list.Add(entity); + } + + // Add to the non-index page list. + results.Add(entity); } - public CreateIndexEntities WithCreateIndexEntity( - Func, Entity>? callback) + // Go through all the index pages and update them. We get a list of + // all the pages in the index and pass them into the function to + // update the existing index. Then we update the entity and add it + // to the bottom of the results list. + foreach ((TIndexKey key, Entity? oldIndex) in indexes) { - this.CreateIndexEntity = callback; - return this; + if (!indexed.TryGetValue(key, out List? list)) + { + list = new List(); + } + + Entity newEntity = this.UpdateIndexEntity!(oldIndex, key, list); + + results.Add(newEntity); } - public CreateIndexEntities WithGetIndexEntityKey( - Func? callback) + // Go through all the known index keys and create the missing pages. + int created = 0; + + foreach ((TIndexKey key, List? list) in indexed) { - this.GetIndexEntityKey = callback; - return this; + // See if we already have a page, if we do, then we've already + // processed that page and don't have to do anything. + if (indexes.ContainsKey(key)) + { + continue; + } + + // We don't have that page and need to add it to the list. + Entity entity = this.CreateIndexEntity!(key, list); + + created++; + results.Add(entity); } - public CreateIndexEntities WithGetIndexKeys( - Func>? callback) - { - this.GetIndexKeys = callback; - return this; - } + // Return the combined together version. + this.logger.Debug( + "Found {Old:N0} and created {New:N0} index pages for {Keys:N0} keys", + indexes.Count, + created, + indexed.Count); - public CreateIndexEntities WithUpdateIndexEntity( - Func, Entity>? callback) - { - this.UpdateIndexEntity = callback; - return this; - } + return results; + } + + public CreateIndexEntities WithCreateIndexEntity(Func, Entity>? callback) + { + this.CreateIndexEntity = callback; + return this; + } + + public CreateIndexEntities WithGetIndexEntityKey(Func? callback) + { + this.GetIndexEntityKey = callback; + return this; + } + + public CreateIndexEntities WithGetIndexKeys(Func>? callback) + { + this.GetIndexKeys = callback; + return this; + } + + public CreateIndexEntities WithUpdateIndexEntity( + Func, Entity>? callback) + { + this.UpdateIndexEntity = callback; + return this; } } diff --git a/src/Nitride/Entities/CreateIndexEntitiesValidator.cs b/src/Nitride/Entities/CreateIndexEntitiesValidator.cs new file mode 100644 index 0000000..5993004 --- /dev/null +++ b/src/Nitride/Entities/CreateIndexEntitiesValidator.cs @@ -0,0 +1,15 @@ +using FluentValidation; + +namespace Nitride.Entities; + +public class CreateIndexEntitiesValidator : AbstractValidator> + where TIndexKey : notnull +{ + public CreateIndexEntitiesValidator() + { + this.RuleFor(x => x.CreateIndexEntity).NotNull(); + this.RuleFor(x => x.GetIndexKeys).NotNull(); + this.RuleFor(x => x.UpdateIndexEntity).NotNull(); + this.RuleFor(x => x.GetIndexEntityKey).NotNull(); + } +} diff --git a/src/Nitride/Entities/LinkPreviousNextHandler.cs b/src/Nitride/Entities/LinkPreviousNextHandler.cs index 3e01fc2..553d3c8 100644 --- a/src/Nitride/Entities/LinkPreviousNextHandler.cs +++ b/src/Nitride/Entities/LinkPreviousNextHandler.cs @@ -1,9 +1,5 @@ using Gallium; -namespace Nitride.Entities -{ - public delegate Entity LinkPreviousNextHandler( - Entity entity, - Entity? previous, - Entity? next); -} +namespace Nitride.Entities; + +public delegate Entity LinkPreviousNextHandler(Entity entity, Entity? previous, Entity? next); diff --git a/src/Nitride/Entities/LinkSerialEntities.cs b/src/Nitride/Entities/LinkSerialEntities.cs index 16e0988..53d584c 100644 --- a/src/Nitride/Entities/LinkSerialEntities.cs +++ b/src/Nitride/Entities/LinkSerialEntities.cs @@ -1,42 +1,52 @@ using System.Collections.Generic; using System.Linq; + +using FluentValidation; + using Gallium; -namespace Nitride.Entities +namespace Nitride.Entities; + +/// +/// Links a series of entities together in some manner. This assumes that +/// the entities coming into the operation are already ordered. +/// +[WithProperties] +public partial class LinkSerialEntities : OperationBase { - /// - /// Links a series of entities together in some manner. This assumes that - /// the entities coming into the operation are already ordered. - /// - [WithProperties] - public partial class LinkSerialEntities : NitrideOperationBase + private readonly IValidator validator; + + public LinkSerialEntities(IValidator validator) { - /// - /// The callback function that sets the previous and next - /// - public LinkPreviousNextHandler? UpdatePreviousNext { get; set; } + this.validator = validator; + } - /// - public override IEnumerable Run(IEnumerable input) + /// + /// The callback function that sets the previous and next + /// + public LinkPreviousNextHandler? UpdatePreviousNext { get; set; } + + /// + public override IEnumerable Run(IEnumerable input) + { + // Make sure everything is good. + this.validator.ValidateAndThrow(this); + + // We pull all the entities into a list so we can walk through them + // more than once along with index access. + var list = input.ToList(); + + // Go through the list and assign each entity in order. + for (int i = 0; i < list.Count; i++) { - // We pull all the entities into a list so we can walk through them - // more than once along with index access. - var list = input.ToList(); + Entity entity = list[i]; + Entity? previous = i > 0 ? list[i - 1] : null; + Entity? next = i < list.Count - 1 ? list[i + 1] : null; - // Go through the list and assign each entity in order. - this.CheckNotNull(x => this.UpdatePreviousNext); - - for (int i = 0; i < list.Count; i++) - { - Entity entity = list[i]; - Entity? previous = i > 0 ? list[i - 1] : null; - Entity? next = i < list.Count - 1 ? list[i + 1] : null; - - list[i] = this.UpdatePreviousNext!(entity, previous, next); - } - - // Return the resulting list. - return list; + list[i] = this.UpdatePreviousNext!(entity, previous, next); } + + // Return the resulting list. + return list; } } diff --git a/src/Nitride/Entities/LinkSerialEntitiesValidator.cs b/src/Nitride/Entities/LinkSerialEntitiesValidator.cs new file mode 100644 index 0000000..d505ced --- /dev/null +++ b/src/Nitride/Entities/LinkSerialEntitiesValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace Nitride.Entities; + +public class LinkSerialEntitiesValidator : AbstractValidator +{ + public LinkSerialEntitiesValidator() + { + this.RuleFor(x => x.UpdatePreviousNext).NotNull(); + } +} diff --git a/src/Nitride/Entities/README.md b/src/Nitride/Entities/README.md index 17f320b..519c714 100644 --- a/src/Nitride/Entities/README.md +++ b/src/Nitride/Entities/README.md @@ -48,34 +48,34 @@ Assert.Equal(2, newEntity.Count); The basic operations for entity components are: -- `Add(component)`: Adds a component as the given type. If there is - already a component there, an exception will be thrown. -- `Add(component)`: As `Add(component)` but the `TType` is the same as - `component.GetType()`. -- `Remove()`: Removes any component of the given type, if exists. If - there is no such component, then nothing happens. -- `Remove(component)`: Same as `Remove` with the component given - determining the `TType`. -- `Set(component)`: Adds or updates a component of the given type. -- `Set(component)`: Same as `Set` with the component given determining - the `TType`. -- `Copy()`: Creates a copy of the entity and assigns it a new identifier. -- `ExactCopy()`: Creates a copy of the entity with the same identifier. +- `Add(component)`: Adds a component as the given type. If there is + already a component there, an exception will be thrown. +- `Add(component)`: As `Add(component)` but the `TType` is the same as + `component.GetType()`. +- `Remove()`: Removes any component of the given type, if exists. If + there is no such component, then nothing happens. +- `Remove(component)`: Same as `Remove` with the component given + determining the `TType`. +- `Set(component)`: Adds or updates a component of the given type. +- `Set(component)`: Same as `Set` with the component given determining + the `TType`. +- `Copy()`: Creates a copy of the entity and assigns it a new identifier. +- `ExactCopy()`: Creates a copy of the entity with the same identifier. As above, all of these return a new entity (or the same one if no change is made to the entity). ### Query Components -- `bool Has()`: Returns a value indicating whether the entity has the - given component type. -- `TType Get()`: Returns the value of the registered component. If there - is no such object, this will throw an exception. -- `TType? GetOptional()`: Returns the value of the registered component. - If there is no such object, this will return the default value. -- `bool TryGet(out TType component)`: Attempt to get the component. If it - cannot be retrieved, then this will return `false` and `component` is - undefined. +- `bool Has()`: Returns a value indicating whether the entity has the + given component type. +- `TType Get()`: Returns the value of the registered component. If there + is no such object, this will throw an exception. +- `TType? GetOptional()`: Returns the value of the registered component. + If there is no such object, this will return the default value. +- `bool TryGet(out TType component)`: Attempt to get the component. If it + cannot be retrieved, then this will return `false` and `component` is + undefined. ## Collections diff --git a/src/Nitride/ExpressionHelper.cs b/src/Nitride/ExpressionHelper.cs index 96be6bc..e42bb71 100644 --- a/src/Nitride/ExpressionHelper.cs +++ b/src/Nitride/ExpressionHelper.cs @@ -3,67 +3,62 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -namespace Nitride +namespace Nitride; + +/// +/// Helper methods for getting the names of properties. +/// +public static class ExpressionHelper { - /// - /// Helper methods for getting the names of properties. - /// - public static class ExpressionHelper + public static string GetMemberName(Expression> expression) { - public static string GetMemberName( - Expression> expression) + return GetMemberName(expression.Body); + } + + public static IEnumerable GetMemberNames(params Expression>[] expressions) + { + return expressions.Select(x => GetMemberName(x.Body)); + } + + public static IEnumerable GetMemberNames( + this T instance, + params Expression>[] expressions) + { + return GetMemberNames(expressions); + } + + private static string GetMemberName(Expression expression) + { + if (expression == null) { - return GetMemberName(expression.Body); - } - - public static IEnumerable GetMemberNames( - params Expression>[] expressions) - { - return expressions - .Select(x => GetMemberName(x.Body)); - } - - public static IEnumerable GetMemberNames( - this T instance, - params Expression>[] expressions) - { - return GetMemberNames(expressions); - } - - private static string GetMemberName(Expression expression) - { - if (expression == null) - { - throw new ArgumentException(nameof(expression)); - } - - if (expression is MemberExpression memberExpression) - { - return memberExpression.Member.Name; - } - - if (expression is MethodCallExpression methodCallExpression) - { - return methodCallExpression.Method.Name; - } - - if (expression is UnaryExpression unaryExpression) - { - return GetMemberName(unaryExpression); - } - throw new ArgumentException(nameof(expression)); } - private static string GetMemberName(UnaryExpression unaryExpression) + if (expression is MemberExpression memberExpression) { - if (unaryExpression.Operand is MethodCallExpression - methodCallExpression) - { - return methodCallExpression.Method.Name; - } - - return ((MemberExpression)unaryExpression.Operand).Member.Name; + return memberExpression.Member.Name; } + + if (expression is MethodCallExpression methodCallExpression) + { + return methodCallExpression.Method.Name; + } + + if (expression is UnaryExpression unaryExpression) + { + return GetMemberName(unaryExpression); + } + + throw new ArgumentException(nameof(expression)); + } + + private static string GetMemberName(UnaryExpression unaryExpression) + { + if (unaryExpression.Operand is MethodCallExpression methodCallExpression) + { + return methodCallExpression.Method.Name; + } + + return ((MemberExpression)unaryExpression.Operand).Member.Name; } } diff --git a/src/Nitride/INitrideOperation.cs b/src/Nitride/INitrideOperation.cs deleted file mode 100644 index c1d83a7..0000000 --- a/src/Nitride/INitrideOperation.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using Gallium; - -namespace Nitride -{ - public interface INitrideOperation - { - /// - /// Runs the input entities through the operation and returns the results. - /// - /// - /// - IEnumerable Run(IEnumerable input); - } -} diff --git a/src/Nitride/IOperation.cs b/src/Nitride/IOperation.cs new file mode 100644 index 0000000..1865e25 --- /dev/null +++ b/src/Nitride/IOperation.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +using Gallium; + +namespace Nitride; + +public interface IOperation +{ + /// + /// Runs the input entities through the operation and returns the results. + /// + /// + /// + IEnumerable Run(IEnumerable input); +} diff --git a/src/Nitride/IOperationBuilder.cs b/src/Nitride/IOperationBuilder.cs new file mode 100644 index 0000000..4fbee6e --- /dev/null +++ b/src/Nitride/IOperationBuilder.cs @@ -0,0 +1,5 @@ +namespace Nitride; + +public interface IOperationBuilder +{ +} diff --git a/src/Nitride/Nitride.csproj b/src/Nitride/Nitride.csproj index 1c4e8d7..ffca3ac 100644 --- a/src/Nitride/Nitride.csproj +++ b/src/Nitride/Nitride.csproj @@ -1,40 +1,47 @@ - - net5.0 - enable - - - - A static site generator written using Gallium ECS. - + + net6.0 + enable + - - - - - - - - - - - - - - - + + A static site generator written using Gallium ECS. + - - - True - + + + $(NoWarn);NU5104 + - - - Analyzer - False - - + + + + + + + + + + + + + + + + + + + + + True + + + + + Analyzer + False + + diff --git a/src/Nitride/NitrideBuilder.cs b/src/Nitride/NitrideBuilder.cs index 00dc5c7..c0fa46c 100644 --- a/src/Nitride/NitrideBuilder.cs +++ b/src/Nitride/NitrideBuilder.cs @@ -1,196 +1,166 @@ using System; using System.Collections.Generic; using System.IO; +using System.Reflection; using System.Threading.Tasks; + using Autofac; -using Autofac.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; + +using MfGames.ToolBuilder; + using Serilog; + using Zio; using Zio.FileSystems; -namespace Nitride +namespace Nitride; + +/// +/// A class that implements a builder pattern for gathering all the +/// components of a static website. Once everything is build, then calling +/// `Build()` will generate the resulting website. +/// +public class NitrideBuilder { + private readonly string[] arguments; + + private readonly List> configureContainerCallbacks; + /// - /// A class that implements a builder pattern for gathering all the - /// components of a static website. Once everything is build, then calling - /// `Build()` will generate the resulting website. + /// An event that is called after the container is built but before + /// the application runs. /// - public class NitrideBuilder + private readonly List> configureSiteCallbacks; + + private readonly NitrideModule nitrideModule; + + public NitrideBuilder(string[] arguments) { - private readonly string[] arguments; + this.arguments = arguments; + this.configureSiteCallbacks = new List>(); + this.configureContainerCallbacks = new List>(); + this.nitrideModule = new NitrideModule(); + this.nitrideModule.ApplicationName = Assembly.GetExecutingAssembly().GetName().Name!; + } - private readonly List> - configureContainerCallbacks; + /// + /// Gets or sets the input directory to automatically register as the + /// source of the files. If this is not set, then no Zio.IFileSystem + /// will be registered and it will have to be done manually. + /// + public DirectoryInfo? RootDirectory { get; set; } - /// - /// An event that is called after the container is built but before - /// the application runs. - /// - private readonly List> - configureSiteCallbacks; + /// + /// Allows for configuration of the Autofac container to register + /// additional types, pipelines, and modules. + /// + /// The callback to configure the container. + /// The builder to chain operations. + public NitrideBuilder ConfigureContainer(Action callback) + { + this.configureContainerCallbacks.Add(callback); + return this; + } - private NitrideLoggingBuilder loggingBuilder; + /// + /// Registers a callback to be called after the container is built but + /// before the application runs. + /// + /// The callback to register. + /// The builder for chaining. + public NitrideBuilder ConfigureSite(Action callback) + { + this.configureSiteCallbacks.Add(callback); + return this; + } - public NitrideBuilder(string[] arguments) + /// + /// Generates or builds the resulting website based on the given + /// command. + /// + /// The task once completed. + public async Task RunAsync() + { + if (this.nitrideModule.ApplicationName == null) { - this.arguments = arguments; - this.loggingBuilder = new NitrideLoggingBuilder(); - this.configureSiteCallbacks = - new List>(); - this.configureContainerCallbacks = - new List>(); + throw new InvalidOperationException( + "Application name must be set, such as with the NitrideBuilder.WithApplicationName() to properly run."); } - /// - /// Gets or sets the builder used to set up logging. - /// - /// - public NitrideLoggingBuilder LoggingBuilder + return await ToolBoxBuilder.Create(this.nitrideModule.ApplicationName, this.arguments) + .ConfigureContainer(this.ConfigureContainer) + .Build() + .RunAsync(); + } + + /// + /// Sets the description of the builder. + /// + public NitrideBuilder WithApplicationDescription(string value) + { + this.nitrideModule.Description = value; + return this; + } + + /// + /// Sets the name of the application, which is displayed in the help screen. + /// + public NitrideBuilder WithApplicationName(string value) + { + this.nitrideModule.ApplicationName = value; + return this; + } + + /// + /// Sets the root directory to a common value by creating a + /// IFileSystem (from Zio) of the root directory and registering it. + /// This will be used for both input and output. + /// + /// + /// The path to the directory that represents "/" while + /// building. + /// + /// The builder to chain calls. + public NitrideBuilder WithRootDirectory(DirectoryInfo directory) + { + this.RootDirectory = directory; + return this; + } + + private void ConfigureContainer(ContainerBuilder builder) + { + // Hook up the rest of the modules. + builder.RegisterModule(this.nitrideModule); + + // Set up our file system. + // TODO Wrong logger + this.RegisterRootDirectory(Log.Logger, builder); + + // Finish up the registration by running our events. + foreach (Action? callback in this.configureSiteCallbacks) { - get => this.loggingBuilder; - set => this.loggingBuilder = - value ?? throw new ArgumentNullException(nameof(value)); + builder.RegisterBuildCallback(scope => callback(this, scope)); } - /// - /// Gets or sets the input directory to automatically register as the - /// source of the files. If this is not set, then no Zio.IFileSystem - /// will be registered and it will have to be done manually. - /// - public DirectoryInfo? RootDirectory { get; set; } - - /// - /// Generates or builds the resulting website based on the given - /// command. - /// - /// The task once completed. - public async Task BuildAsync() + foreach (Action? configureContainer in this.configureContainerCallbacks) { - await Host - .CreateDefaultBuilder(this.arguments) - .UseSerilog() - .UseServiceProviderFactory(new AutofacServiceProviderFactory()) - .ConfigureServices(this.ConfigureServices) - .ConfigureContainer(this.ConfigureContainer) - .RunConsoleAsync(); - - return 0; - } - - /// - /// Allows for configuration of the Autofac container to register - /// additional types, pipelines, and modules. - /// - /// The callback to configure the container. - /// The builder to chain operations. - public NitrideBuilder ConfigureContainer( - Action callback) - { - this.configureContainerCallbacks.Add(callback); - return this; - } - - /// - /// Overrides the logging configuration for customization. - /// - /// - /// The builder to chain methods. - public NitrideBuilder ConfigureLogging( - Func callback) - { - this.loggingBuilder.Callback = callback; - return this; - } - - /// - /// Registers a callback to be called after the container is built but - /// before the application runs. - /// - /// The callback to register. - /// The builder for chaining. - public NitrideBuilder ConfigureSite( - Action callback) - { - this.configureSiteCallbacks.Add(callback); - return this; - } - - /// - /// Sets the root directory to a common value by creating a - /// IFileSystem (from Zio) of the root directory and registering it. - /// This will be used for both input and output. - /// - /// - /// The path to the directory that represents "/" while - /// building. - /// - /// The builder to chain calls. - public NitrideBuilder WithRootDirectory(DirectoryInfo directory) - { - this.RootDirectory = directory; - return this; - } - - private void ConfigureContainer(ContainerBuilder builder) - { - // We want to get logging up and running as soon as possible. We - // also hook up the logging to the process exit in an attempt to - // make sure the logger is properly flushed before exiting. - ILogger logger = this.loggingBuilder - .Setup(builder) - .ForContext(); - - AppDomain.CurrentDomain.ProcessExit += - (_, _) => Log.CloseAndFlush(); - - // Hook up the rest of the modules. - builder.RegisterModule(); - - // Set up our file system. - this.RegisterRootDirectory(logger, builder); - - // Finish up the registration by running our events. - foreach (var callback in this.configureSiteCallbacks) - { - builder.RegisterBuildCallback(scope => callback(this, scope)); - } - - foreach (var configureContainer in this.configureContainerCallbacks) - { - configureContainer.Invoke(builder); - } - } - - private void ConfigureServices(IServiceCollection services) - { - services.AddAutofac(); - services.AddHostedService(); - } - - private void RegisterRootDirectory( - ILogger logger, - ContainerBuilder builder) - { - if (this.RootDirectory == null) - { - logger.Verbose("No root directory is registered"); - return; - } - - logger.Debug( - "Setting root directory to {Path}", - this.RootDirectory.FullName); - - var rootFileSystem = new PhysicalFileSystem(); - var subFileSystem = new SubFileSystem( - rootFileSystem, - this.RootDirectory.FullName); - - builder.RegisterInstance(subFileSystem) - .As() - .SingleInstance(); + configureContainer.Invoke(builder); } } + + private void RegisterRootDirectory(ILogger logger, ContainerBuilder builder) + { + if (this.RootDirectory == null) + { + logger.Verbose("No root directory is registered"); + return; + } + + logger.Debug("Setting root directory to {Path}", this.RootDirectory.FullName); + + var rootFileSystem = new PhysicalFileSystem(); + var subFileSystem = new SubFileSystem(rootFileSystem, this.RootDirectory.FullName); + + builder.RegisterInstance(subFileSystem).As().SingleInstance(); + } } diff --git a/src/Nitride/NitrideLoggingBuilder.cs b/src/Nitride/NitrideLoggingBuilder.cs deleted file mode 100644 index 53a8c66..0000000 --- a/src/Nitride/NitrideLoggingBuilder.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using Autofac; -using Serilog; -using Serilog.Core; -using Serilog.Events; - -namespace Nitride -{ - /// - /// The internal builder for setting up logging. - /// - public class NitrideLoggingBuilder - { - public NitrideLoggingBuilder() - { - this.LevelSwitch = new LoggingLevelSwitch(); - } - - /// - /// Used when the user uses .ConfigureLogging() to configure the logger - /// instead of overriding the function. - /// - public Func? Callback { get; set; } - - /// - /// Gets the level switch used to control the logging verbosity from - /// the command-line. - /// - public virtual LoggingLevelSwitch LevelSwitch { get; } - - /// - /// Sets up logging and registers it under Serilog.ILogger in the - /// container. - /// - /// The Autofac builder for the container. - /// The logger configured during the setup. - public virtual ILogger Setup(ContainerBuilder builder) - { - // Figure out how we are going to configure the logger. - var configuration = new LoggerConfiguration(); - - // Create the logger and register it into the builder. - ILogger logger = this.Callback?.Invoke(configuration) - ?? this.CreateLogger(configuration); - - builder - .RegisterInstance(logger) - .As() - .SingleInstance(); - builder - .RegisterInstance(this.LevelSwitch) - .AsSelf() - .SingleInstance(); - - return logger; - } - - /// - /// Creates a Serilog.ILogger and returns the resulting logger to be - /// used within the system. This is one of the more common places to - /// customize logging. - /// - /// - /// - protected virtual ILogger CreateLogger( - LoggerConfiguration configuration) - { - const string Template = - "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] " - + "({SourceContext}) {Message}" - + "{NewLine}{Exception}"; - - return configuration - .MinimumLevel.ControlledBy(this.LevelSwitch) - .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) - .Enrich.FromLogContext() - .WriteTo.Console(outputTemplate: Template) - .CreateLogger(); - } - } -} diff --git a/src/Nitride/NitrideModule.cs b/src/Nitride/NitrideModule.cs index 4677ec6..7b5568d 100644 --- a/src/Nitride/NitrideModule.cs +++ b/src/Nitride/NitrideModule.cs @@ -1,29 +1,68 @@ +using System.Collections.Generic; +using System.CommandLine; + using Autofac; + +using Nitride.Commands; using Nitride.Entities; using Nitride.Pipelines; -namespace Nitride +namespace Nitride; + +public class NitrideModule : Module { - public class NitrideModule : Module + public string? ApplicationName { get; set; } + + /// + /// Gets or sets the description of the website. + /// + public string? Description { get; set; } + + /// + protected override void Load(ContainerBuilder builder) { - /// - protected override void Load(ContainerBuilder builder) - { - builder - .RegisterAssemblyTypes(this.GetType().Assembly) - .Except() - .Except() - .AsSelf() - .AsImplementedInterfaces() - .SingleInstance(); + // Pipelines + builder.RegisterType().AsSelf(); + builder.RegisterType().AsSelf().SingleInstance(); - builder - .RegisterType() - .AsSelf(); + // Operations + builder.RegisterValidators(this); + builder.RegisterOperators(this); + builder.RegisterGeneric(typeof(CreateIndexEntities<>)).As(typeof(CreateIndexEntities<>)); - builder - .RegisterGeneric(typeof(CreateIndexEntities<>)) - .As(typeof(CreateIndexEntities<>)); - } + // Commands + builder.RegisterType().AsSelf().As().SingleInstance(); + + // MfGames.ToolBuilder requires the RootCommand to be registered. This is because + // of various things, mostly coordinating between different systems. + builder.Register( + c => + { + // Create the new root command. + var root = new RootCommand(); + + if (!string.IsNullOrWhiteSpace(this.ApplicationName)) + { + root.Name = this.ApplicationName; + } + + if (!string.IsNullOrWhiteSpace(this.Description)) + { + root.Description = this.Description; + } + + ; + + // Add in the commands. + IEnumerable commands = c.Resolve>(); + + foreach (Command command in commands) + { + root.AddCommand(command); + } + + return root; + }) + .AsSelf(); } } diff --git a/src/Nitride/NitrideModuleBase.cs b/src/Nitride/NitrideModuleBase.cs deleted file mode 100644 index 390530f..0000000 --- a/src/Nitride/NitrideModuleBase.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using Autofac; -using Nitride.Registration; -using Module = Autofac.Module; - -namespace Nitride -{ - /// - /// Common functionality for Nitride modules. - /// - public abstract class NitrideModuleBase : Module - { - private static readonly HashSet Initialized = new(); - - /// - protected sealed override void Load(ContainerBuilder builder) - { - // We need to allow a module to be registered multiple times because - // some modules will force inclusion of their requirements which the - // user may also add. - Type type = this.GetType(); - Assembly assembly = type.Assembly; - string typeKey = type.FullName!; - - if (Initialized.Contains(typeKey)) - { - return; - } - - Initialized.Add(typeKey); - - // Register the common types. - builder - .RegisterAssemblyTypes(assembly) - .Where(CanRegister) - .Where(IsNotSingleton) - .AsSelf() - .AsImplementedInterfaces(); - - builder - .RegisterAssemblyTypes(assembly) - .Where(CanRegister) - .Where(IsSingleton) - .AsSelf() - .AsImplementedInterfaces() - .SingleInstance(); - - this.Register(builder); - } - - /// - /// Perform additional registrations after the common one. - /// - /// The builder to configure. - protected virtual void Register(ContainerBuilder builder) - { - } - - private static bool CanRegister(Type x) - { - return x.GetCustomAttribute() == null; - } - - private static bool IsNotSingleton(Type x) - { - return x.GetCustomAttribute() == null; - } - - private static bool IsSingleton(Type x) - { - return !IsNotSingleton(x); - } - } -} diff --git a/src/Nitride/NitrideModuleExtensions.cs b/src/Nitride/NitrideModuleExtensions.cs new file mode 100644 index 0000000..540587b --- /dev/null +++ b/src/Nitride/NitrideModuleExtensions.cs @@ -0,0 +1,18 @@ +using Autofac; + +using FluentValidation; + +namespace Nitride; + +public static class NitrideModuleExtensions +{ + public static void RegisterOperators(this ContainerBuilder builder, Module module) + { + builder.RegisterAssemblyTypes(module.GetType().Assembly).Where(x => x.IsAssignableTo()).AsSelf(); + } + + public static void RegisterValidators(this ContainerBuilder builder, Module module) + { + builder.RegisterAssemblyTypes(module.GetType().Assembly).AsClosedTypesOf(typeof(IValidator<>)); + } +} diff --git a/src/Nitride/NitrideOperationBase.cs b/src/Nitride/NitrideOperationBase.cs deleted file mode 100644 index 29a8074..0000000 --- a/src/Nitride/NitrideOperationBase.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Collections.Generic; -using Gallium; - -namespace Nitride -{ - /// - /// Contains common functionality useful for Nitride operations. - /// - public abstract class NitrideOperationBase : INitrideOperation - { - /// - /// A method that makes sure a property is set with a useful message. - /// If the value is not null, then no exception is thrown. - /// - /// The name of the property. - /// The object to check for null. - /// Thrown if the parameter is null. - public void CheckNotNull(string name, object? parameter) - { - if (parameter == null) - { - throw new InvalidOperationException( - this.GetType().Name - + " did not have the " - + name - + " property set before calling Run(). Set this via the " - + name - + "property or by calling Set" - + name - + "."); - } - } - - /// - public abstract IEnumerable Run(IEnumerable input); - } -} diff --git a/src/Nitride/NitrideOperationBaseExtensions.cs b/src/Nitride/NitrideOperationBaseExtensions.cs deleted file mode 100644 index 3949852..0000000 --- a/src/Nitride/NitrideOperationBaseExtensions.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Linq.Expressions; - -namespace Nitride -{ - public static class NitrideOperationBaseExtensions - { - public static void CheckNotNull( - this TType instance, - Expression> expression) - where TType : NitrideOperationBase - { - // We can't implement this inside the class because of C# - // limitations. - string name = ExpressionHelper.GetMemberName(expression); - object? value = expression.Compile()(instance); - - instance.CheckNotNull(name, value); - } - } -} diff --git a/src/Nitride/NitrideOperationExtensions.cs b/src/Nitride/NitrideOperationExtensions.cs index 080e609..9f1d187 100644 --- a/src/Nitride/NitrideOperationExtensions.cs +++ b/src/Nitride/NitrideOperationExtensions.cs @@ -1,25 +1,23 @@ using System.Collections.Generic; + using Gallium; -namespace Nitride +namespace Nitride; + +/// +/// Extension methods to run a Nitride operation inline with code. +/// +public static class NitrideOperationExtensions { /// - /// Extension methods to run a Nitride operation inline with code. + /// Runs the given configured operation against the input and returns + /// the results. /// - public static class NitrideOperationExtensions + /// The entities to perform the operation against. + /// The operation to run. + /// The results of the operation. + public static IEnumerable Run(this IEnumerable input, IOperation operation) { - /// - /// Runs the given configured operation against the input and returns - /// the results. - /// - /// The entities to perform the operation against. - /// The operation to run. - /// The results of the operation. - public static IEnumerable Run( - this IEnumerable input, - INitrideOperation operation) - { - return operation.Run(input); - } + return operation.Run(input); } } diff --git a/src/Nitride/NitrideService.cs b/src/Nitride/NitrideService.cs deleted file mode 100644 index f5c1723..0000000 --- a/src/Nitride/NitrideService.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.Collections.Generic; -using System.CommandLine; -using System.CommandLine.Parsing; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; -using Nitride.Commands; -using Serilog; -using Serilog.Core; -using Serilog.Events; - -namespace Nitride -{ - /// - /// Implements the command-line shell for generating the static site. - /// - public class NitrideService : IHostedService - { - private readonly IList commands; - - private readonly IHostApplicationLifetime lifetime; - - private readonly Option logEventLevelOption; - - private readonly ILogger logger; - - private readonly LoggingLevelSwitch loggingLevelSwitch; - - private int exitCode; - - public NitrideService( - ILogger logger, - IHostApplicationLifetime lifetime, - IList commands, - LoggingLevelSwitch loggingLevelSwitch) - { - this.lifetime = lifetime; - this.commands = commands; - this.loggingLevelSwitch = loggingLevelSwitch; - this.logger = logger.ForContext(); - - this.logEventLevelOption = new Option( - "--log-level", - () => LogEventLevel.Information, - "Controls the verbosity of the output"); - } - - /// - public Task StartAsync(CancellationToken cancellationToken) - { - this.lifetime.ApplicationStarted - .Register( - () => - { - Task.Run( - async () => { await this.RunAsync(); }, - cancellationToken); - }); - - return Task.CompletedTask; - } - - /// - public Task StopAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - - private RootCommand CreateRootCommand() - { - // Create the root command and add in the top-level commands - // underneath it. - var root = new RootCommand(); - - foreach (var command in this.commands) - { - root.AddCommand((Command)command); - } - - // Add the universal options. - root.AddGlobalOption(this.logEventLevelOption); - - // Return the resulting container. - return root; - } - - private async Task RunAsync() - { - try - { - // Build the command tree. - RootCommand root = this.CreateRootCommand(); - string[] args = Environment.GetCommandLineArgs(); - - // Parse the results so we can pull out our globals. - ParseResult results = root.Parse(args); - LogEventLevel logEventLevel = results - .ValueForOption(this.logEventLevelOption); - - this.loggingLevelSwitch.MinimumLevel = logEventLevel; - - // Execute the command. - this.logger.Verbose("Running the command-line arguments"); - this.exitCode = await root.InvokeAsync(args); - } - catch (Exception exception) - { - this.logger.Error( - exception, - "Unhandled exception!"); - } - finally - { - // Stop the application once the work is done. - this.lifetime.StopApplication(); - } - } - } -} diff --git a/src/Nitride/OperationBase.cs b/src/Nitride/OperationBase.cs new file mode 100644 index 0000000..6652d60 --- /dev/null +++ b/src/Nitride/OperationBase.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +using Gallium; + +namespace Nitride; + +/// +/// Contains common functionality useful for Nitride operations. +/// +public abstract class OperationBase : IOperation +{ + /// + public abstract IEnumerable Run(IEnumerable input); +} diff --git a/src/Nitride/Pipelines/IPipeline.cs b/src/Nitride/Pipelines/IPipeline.cs index 8dc069e..5255b86 100644 --- a/src/Nitride/Pipelines/IPipeline.cs +++ b/src/Nitride/Pipelines/IPipeline.cs @@ -1,28 +1,28 @@ using System.Collections.Generic; using System.Threading.Tasks; + using Gallium; -namespace Nitride.Pipelines +namespace Nitride.Pipelines; + +/// +/// Implements the basic signature for a pipeline, a distinct unit of +/// processing that reads, manipulates, and writes data. +/// +public interface IPipeline { /// - /// Implements the basic signature for a pipeline, a distinct unit of - /// processing that reads, manipulates, and writes data. + /// Gets the dependencies for the pipeline. /// - public interface IPipeline - { - /// - /// Gets the dependencies for the pipeline. - /// - IEnumerable GetDependencies(); + IEnumerable GetDependencies(); - /// - /// Performs various operations such as reading, writing, and - /// transformation on the given entities before returning a new - /// collection, which may or may not include some of the original - /// entities. - /// - /// The entities to process. - /// The resulting entities after the process runs. - Task> RunAsync(IEnumerable entities); - } + /// + /// Performs various operations such as reading, writing, and + /// transformation on the given entities before returning a new + /// collection, which may or may not include some of the original + /// entities. + /// + /// The entities to process. + /// The resulting entities after the process runs. + Task> RunAsync(IEnumerable entities); } diff --git a/src/Nitride/Pipelines/PipelineBase.cs b/src/Nitride/Pipelines/PipelineBase.cs index 889fce7..45ebc46 100644 --- a/src/Nitride/Pipelines/PipelineBase.cs +++ b/src/Nitride/Pipelines/PipelineBase.cs @@ -1,41 +1,40 @@ using System.Collections.Generic; using System.Threading.Tasks; + using Gallium; -namespace Nitride.Pipelines +namespace Nitride.Pipelines; + +/// +/// A basic pipeline that is configured through properties and methods. +/// +public abstract class PipelineBase : IPipeline { - /// - /// A basic pipeline that is configured through properties and methods. - /// - public abstract class PipelineBase : IPipeline + private readonly List dependencies; + + protected PipelineBase() { - private readonly List dependencies; + this.dependencies = new List(); + } - protected PipelineBase() - { - this.dependencies = new List(); - } + public PipelineBase AddDependency(IPipeline pipeline) + { + this.dependencies.Add(pipeline); + return this; + } - public PipelineBase AddDependency(IPipeline pipeline) - { - this.dependencies.Add(pipeline); - return this; - } + /// + public IEnumerable GetDependencies() + { + return this.dependencies; + } - /// - public IEnumerable GetDependencies() - { - return this.dependencies; - } + /// + public abstract Task> RunAsync(IEnumerable entities); - /// - public abstract Task> RunAsync( - IEnumerable entities); - - /// - public override string ToString() - { - return this.GetType().Name; - } + /// + public override string ToString() + { + return this.GetType().Name; } } diff --git a/src/Nitride/Pipelines/PipelineManager.cs b/src/Nitride/Pipelines/PipelineManager.cs index 5cf2974..29c09c9 100644 --- a/src/Nitride/Pipelines/PipelineManager.cs +++ b/src/Nitride/Pipelines/PipelineManager.cs @@ -2,183 +2,152 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; + using Humanizer; + using Serilog; -namespace Nitride.Pipelines +namespace Nitride.Pipelines; + +/// +/// A manager class for all of the pipelines. This class is responsible for +/// hooking everything up and handling the ordering of pipelines as they +/// are run. +/// +public class PipelineManager { - /// - /// A manager class for all of the pipelines. This class is responsible for - /// hooking everything up and handling the ordering of pipelines as they - /// are run. - /// - public class PipelineManager + private readonly PipelineRunner.Factory createEntry; + + private readonly ILogger logger; + + private List entries; + + private bool isSetup; + + private ICollection pipelines; + + public PipelineManager(ILogger logger, IEnumerable pipelines, PipelineRunner.Factory createEntry) { - private readonly PipelineRunner.Factory createEntry; + this.createEntry = createEntry; + this.logger = logger.ForContext(); + this.pipelines = new HashSet(pipelines); + this.entries = null!; + } - private readonly ILogger logger; + public ICollection Pipelines + { + get => this.pipelines; + set => this.pipelines = value ?? throw new ArgumentNullException(nameof(value)); + } - private List entries; + /// + /// Runs all of the pipelines in the appropriate order while running + /// across multiple threads. + /// + /// A task with zero for success or otherwise an error code. + public Task RunAsync() + { + // Make sure everything is setup. + DateTime started = DateTime.UtcNow; - private bool isSetup; - - private ICollection pipelines; - - public PipelineManager( - ILogger logger, - IEnumerable pipelines, - PipelineRunner.Factory createEntry) + if (!this.Setup()) { - this.createEntry = createEntry; - this.logger = logger.ForContext(); - this.pipelines = new HashSet(pipelines); - this.entries = null!; + return Task.FromResult(1); } - public ICollection Pipelines + // Go through all the entries and start each one. We gather the + // resulting tasks and then wait for all of them to end. + this.logger.Verbose("Starting {Count:l}", "pipeline".ToQuantity(this.pipelines.Count)); + + Task[] tasks = this.entries.Select(x => Task.Run(async () => await x.RunAsync())).ToArray(); + var report = TimeSpan.FromSeconds(15); + + while (!Task.WaitAll(tasks, report)) { - get => this.pipelines; - set => this.pipelines = - value ?? throw new ArgumentNullException(nameof(value)); + var waiting = this.entries.Where(x => !x.IsFinished).ToList(); + + this.logger.Debug("Waiting for {Count:l} to finish running", "pipeline".ToQuantity(waiting.Count)); + + IOrderedEnumerable> states = + waiting.GroupBy(x => x.State, x => x).OrderBy(x => (int)x.Key); + + foreach (IGrouping? state in states) + { + var statePipelines = state.OrderBy(x => x.Pipeline.ToString()).ToList(); + + this.logger.Verbose( + "Waiting for {Count:l} in {State}: {List:l}", + "pipeline".ToQuantity(statePipelines.Count), + state.Key, + string.Join( + ", ", + state.Key == PipelineRunnerState.Started + ? statePipelines.Select(x => $"{x.Pipeline} ({x.ElapsedFromState})") + : statePipelines.Select(x => x.Pipeline.ToString()))); + } } - /// - /// Runs all of the pipelines in the appropriate order while running - /// across multiple threads. - /// - /// A task with zero for success or otherwise an error code. - public Task RunAsync() + // Figure out our return code. + bool hasErrors = this.entries.Any(x => x.State == PipelineRunnerState.Errored); + + this.logger.Information("Completed in {Elapsed}", DateTime.UtcNow - started); + + return Task.FromResult(hasErrors ? 2 : 0); + } + + /// + /// Performs the final initialization and preparation for the pipelines + /// and get them ready for deploying. + /// + private bool Setup() + { + // If we've already set up ourselves, then we do nothing. + if (this.isSetup) { - // Make sure everything is setup. - DateTime started = DateTime.UtcNow; - - if (!this.Setup()) - { - return Task.FromResult(1); - } - - // Go through all the entries and start each one. We gather the - // resulting tasks and then wait for all of them to end. - this.logger.Verbose( - "Starting {Count:l}", - "pipeline".ToQuantity(this.pipelines.Count)); - - Task[] tasks = this.entries - .Select(x => Task.Run(async () => await x.RunAsync())) - .ToArray(); - TimeSpan report = TimeSpan.FromSeconds(15); - - while (!Task.WaitAll(tasks, report)) - { - List? waiting = this - .entries.Where(x => !x.IsFinished) - .ToList(); - - this.logger.Debug( - "Waiting for {Count:l} to finish running", - "pipeline".ToQuantity(waiting.Count)); - - IOrderedEnumerable< - IGrouping> states = - waiting - .GroupBy(x => x.State, x => x) - .OrderBy(x => (int)x.Key); - - foreach (var state in states) - { - List statePipelines = state - .OrderBy(x => x.Pipeline.ToString()) - .ToList(); - - this.logger.Verbose( - "Waiting for {Count:l} in {State}: {List:l}", - "pipeline".ToQuantity(statePipelines.Count), - state.Key, - string.Join( - ", ", - state.Key == PipelineRunnerState.Started - ? statePipelines - .Select( - x => - $"{x.Pipeline} ({x.ElapsedFromState})") - : statePipelines - .Select(x => x.Pipeline.ToString()))); - } - } - - // Figure out our return code. - bool hasErrors = this.entries - .Any(x => x.State == PipelineRunnerState.Errored); - - this.logger.Information( - "Completed in {Elapsed}", - DateTime.UtcNow - started); - - return Task.FromResult(hasErrors ? 2 : 0); - } - - /// - /// Performs the final initialization and preparation for the pipelines - /// and get them ready for deploying. - /// - private bool Setup() - { - // If we've already set up ourselves, then we do nothing. - if (this.isSetup) - { - return true; - } - - // If we don't have any pipelines, then we can't process. - if (this.pipelines.Count == 0) - { - this.logger.Error( - "There are no registered pipelines run, use" - + " ConfigureContainer to include IPipeline instances"); - - return false; - } - - this.logger.Verbose( - "Setting up {Count:l}", - "pipeline".ToQuantity(this.pipelines.Count)); - - // Wrap all the pipelines into entries. We do this before the next - // step so we can have the entries depend on the entries. - this.entries = this.pipelines - .Select(x => this.createEntry(x)) - .ToList(); - - // Go through and connect the pipelines together. - foreach (var entry in this.entries) - { - List dependencies = entry.Pipeline - .GetDependencies() - .ToList(); - - foreach (var dependency in dependencies) - { - // Get the entry for the dependency. - PipelineRunner dependencyPipeline = this.entries - .Single(x => x.Pipeline == dependency); - - // Set up the bi-directional connection. - entry.Incoming.Add(dependencyPipeline); - dependencyPipeline.Outgoing.Add(entry); - } - } - - // Loop through all the entries and tell them we are done providing - // and they can set up internal threads other structures. - foreach (var entry in this.entries) - { - entry.Initialize(); - } - - // We have run successfully. - this.isSetup = true; - return true; } + + // If we don't have any pipelines, then we can't process. + if (this.pipelines.Count == 0) + { + this.logger.Error( + "There are no registered pipelines run, use" + " ConfigureContainer to include IPipeline instances"); + + return false; + } + + this.logger.Verbose("Setting up {Count:l}", "pipeline".ToQuantity(this.pipelines.Count)); + + // Wrap all the pipelines into entries. We do this before the next + // step so we can have the entries depend on the entries. + this.entries = this.pipelines.Select(x => this.createEntry(x)).ToList(); + + // Go through and connect the pipelines together. + foreach (PipelineRunner? entry in this.entries) + { + var dependencies = entry.Pipeline.GetDependencies().ToList(); + + foreach (IPipeline? dependency in dependencies) + { + // Get the entry for the dependency. + PipelineRunner dependencyPipeline = this.entries.Single(x => x.Pipeline == dependency); + + // Set up the bi-directional connection. + entry.Incoming.Add(dependencyPipeline); + dependencyPipeline.Outgoing.Add(entry); + } + } + + // Loop through all the entries and tell them we are done providing + // and they can set up internal threads other structures. + foreach (PipelineRunner? entry in this.entries) + { + entry.Initialize(); + } + + // We have run successfully. + this.isSetup = true; + + return true; } } diff --git a/src/Nitride/Pipelines/PipelineRunner.cs b/src/Nitride/Pipelines/PipelineRunner.cs index c938aea..af48e9d 100644 --- a/src/Nitride/Pipelines/PipelineRunner.cs +++ b/src/Nitride/Pipelines/PipelineRunner.cs @@ -3,374 +3,350 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; + using Gallium; + using Humanizer; + using Serilog; -namespace Nitride.Pipelines +namespace Nitride.Pipelines; + +/// +/// A wrapper class to handle a pipeline along with various methods for +/// tracking the operation of the pipeline and thread coordination. +/// +/// +/// This is intended to only be used within a PipelineManager and there +/// should be little reason to use or extend this class. +/// +public class PipelineRunner { /// - /// A wrapper class to handle a pipeline along with various methods for - /// tracking the operation of the pipeline and thread coordination. + /// The manual reset event used to coordinate thread operations. /// - /// - /// This is intended to only be used within a PipelineManager and there - /// should be little reason to use or extend this class. - /// - public class PipelineRunner + private readonly ManualResetEventSlim blockDependencies; + + /// + /// A manual reset event to tell the thread when consumers are done. + /// + private readonly ManualResetEventSlim consumersDone; + + private readonly ILogger logger; + + private DateTime changed; + + private bool signaledDoneWithInputs; + + private DateTime started; + + /// + /// Contains the number of consumers we're currently waiting to finish + /// processing. + /// + private int waitingOnConsumers; + + public PipelineRunner(ILogger logger, IPipeline pipeline) { - /// - /// The manual reset event used to coordinate thread operations. - /// - private readonly ManualResetEventSlim blockDependencies; + this.Pipeline = pipeline ?? throw new ArgumentNullException(nameof(pipeline)); + this.Incoming = new List(); + this.Outgoing = new List(); + this.Outputs = new List(); + this.logger = logger.ForContext(); + this.blockDependencies = new ManualResetEventSlim(false); + this.consumersDone = new ManualResetEventSlim(false); + this.started = DateTime.Now; + this.changed = DateTime.Now; + } - /// - /// A manual reset event to tell the thread when consumers are done. - /// - private readonly ManualResetEventSlim consumersDone; + /// + /// Public factory method for creating a new pipeline manager entry. + /// + public delegate PipelineRunner Factory(IPipeline pipeline); - private readonly ILogger logger; + public TimeSpan ElapsedFromInitialized => DateTime.Now - this.started; - private DateTime changed; + public TimeSpan ElapsedFromState => DateTime.Now - this.changed; - private bool signaledDoneWithInputs; + /// + /// A collection of incoming entries that produce data for the pipeline. + /// + public ICollection Incoming { get; } - private DateTime started; + /// + /// Gets a value indicating whether the output of this entry is not + /// used by any other pipeline. + /// + public bool IsFinal => this.Outgoing.Count == 0; - /// - /// Contains the number of consumers we're currently waiting to finish - /// processing. - /// - private int waitingOnConsumers; + /// + /// Gets a value indicating whether this pipeline is done running. + /// + public bool IsFinished => this.State is PipelineRunnerState.Finalized or PipelineRunnerState.Errored; - public PipelineRunner( - ILogger logger, - IPipeline pipeline) + /// + /// Gets a value indicating whether this entry is a starting one + /// that consumes no data. + /// + public bool IsStarting => this.Incoming.Count == 0; + + /// + /// The collection of outgoing entries that consume the results of + /// the pipeline in this entry. + /// + public ICollection Outgoing { get; } + + /// + /// Contains the list of all the outputs from this pipeline. This is + /// only ensured to be valid after the pipeline is in the `Providing` + /// state. + /// + public List Outputs { get; } + + /// + /// The pipeline associated with the entry. + /// + public IPipeline Pipeline { get; } + + /// + /// Gets the current state of the pipeline. + /// + public PipelineRunnerState State { get; private set; } + + /// + /// A method that tells the pipeline that one of the dependencies has + /// completed consuming the input. + /// + public void ConsumerDoneWithOutputs() + { + int current = Interlocked.Decrement(ref this.waitingOnConsumers); + + this.logger.Verbose("{Pipeline:l}: Consumer signalled, waiting for {Count:n0}", this.Pipeline, current); + + if (current == 0) { - this.Pipeline = pipeline - ?? throw new ArgumentNullException(nameof(pipeline)); - this.Incoming = new List(); - this.Outgoing = new List(); - this.Outputs = new List(); - this.logger = logger.ForContext(); - this.blockDependencies = new ManualResetEventSlim(false); - this.consumersDone = new ManualResetEventSlim(false); - this.started = DateTime.Now; - this.changed = DateTime.Now; - } - - /// - /// Public factory method for creating a new pipeline manager entry. - /// - public delegate PipelineRunner Factory(IPipeline pipeline); - - public TimeSpan ElapsedFromInitialized => DateTime.Now - this.started; - - public TimeSpan ElapsedFromState => DateTime.Now - this.changed; - - /// - /// A collection of incoming entries that produce data for the pipeline. - /// - public ICollection Incoming { get; } - - /// - /// Gets a value indicating whether the output of this entry is not - /// used by any other pipeline. - /// - public bool IsFinal => this.Outgoing.Count == 0; - - /// - /// Gets a value indicating whether this pipeline is done running. - /// - public bool IsFinished => this.State - is PipelineRunnerState.Finalized - or PipelineRunnerState.Errored; - - /// - /// Gets a value indicating whether this entry is a starting one - /// that consumes no data. - /// - public bool IsStarting => this.Incoming.Count == 0; - - /// - /// The collection of outgoing entries that consume the results of - /// the pipeline in this entry. - /// - public ICollection Outgoing { get; } - - /// - /// Contains the list of all the outputs from this pipeline. This is - /// only ensured to be valid after the pipeline is in the `Providing` - /// state. - /// - public List Outputs { get; } - - /// - /// The pipeline associated with the entry. - /// - public IPipeline Pipeline { get; } - - /// - /// Gets the current state of the pipeline. - /// - public PipelineRunnerState State { get; private set; } - - /// - /// A method that tells the pipeline that one of the dependencies has - /// completed consuming the input. - /// - public void ConsumerDoneWithOutputs() - { - int current = Interlocked.Decrement(ref this.waitingOnConsumers); - - this.logger.Verbose( - "{Pipeline:l}: Consumer signalled, waiting for {Count:n0}", - this.Pipeline, - current); - - if (current == 0) - { - this.consumersDone.Set(); - } - } - - /// - /// Initializes the runner after all external properties have been - /// set and configured. - /// - public void Initialize() - { - this.ChangeState(PipelineRunnerState.Initialized); - } - - /// - /// Executes the pipeline, including waiting for any or all - /// dependencies. - /// - public async Task RunAsync() - { - try - { - // Make sure we have a valid state. - switch (this.State) - { - case PipelineRunnerState.Initialized: - case PipelineRunnerState.Finalized: - break; - - default: - this.logger.Error( - "{Pipeline:l}: Pipeline cannot be started in a {State}" - + " state (not Initialized or Finalized)", - this.Pipeline, - this.State); - break; - } - - // Prepare ourselves for running. We have a start/stop state because - // this may be non-zero time. - this.started = DateTime.Now; - this.changed = DateTime.Now; - this.ChangeState(PipelineRunnerState.Preparing); - this.signaledDoneWithInputs = false; - this.ChangeState(PipelineRunnerState.Prepared); - - // Go through the incoming and wait for each of the manual resets - // on the dependency pipelines. - if (this.WaitForDependencies()) - { - this.SignalDoneWithInputs(); - return; - } - - // Grab the outputs from the incoming. They will be populated - // because we have waited for the reset events. - this.ChangeState(PipelineRunnerState.Started); - List input = this.GatherDependencyOutputs(); - - // Run the pipeline. This may not be resolved until we gather - // the output below. - await this.RunPipeline(input); - - // At this point, we are completely done with our inputs, so signal - // to them in case they have to clean up any of their structures. - this.SignalDoneWithInputs(); - - // If we have outgoing runners, provide them data until they are - // done. - this.SendToDependants(); - - // Finalize ourselves. - this.ChangeState(PipelineRunnerState.Finalized); - } - catch (Exception exception) - { - // Report the exception. - this.logger.Error( - exception, - "{Pipeline:l}: There was an exception running pipeline", - this.Pipeline); - - // Change our state and then release any pipeline waiting for us - // so they can pick up the error and fail themselves. - this.ChangeState(PipelineRunnerState.Errored); - this.blockDependencies.Set(); - this.SignalDoneWithInputs(); - } - } - - /// - /// A method to block the call until this runner is done processing and - /// is ready to provide output. - /// - public void WaitUntilProviding() - { - this.blockDependencies.Wait(); - } - - /// - /// Changes the state of the pipeline into a new state. - /// - /// The state to change the pipeline into. - private void ChangeState(PipelineRunnerState newState) - { - this.logger.Verbose( - "{Pipeline:l}: Switching from state {Old} to {New} (elapsed {Elapsed}, duration {Duration})", - this.Pipeline, - this.State, - newState, - this.ElapsedFromInitialized, - this.ElapsedFromState); - this.changed = DateTime.Now; - this.State = newState; - } - - private List GatherDependencyOutputs() - { - if (this.Incoming.Count <= 0) - { - return new List(); - } - - // Report that we are gathering our outputs. - this.logger.Verbose( - "{Pipeline:l}: Gathering outputs from {Count:n0} dependencies", - this.Pipeline, - this.Incoming.Count); - - List input = this.Incoming - .SelectMany(x => x.Outputs) - .ToList(); - - this.logger.Debug( - "{Pipeline:l}: Got {Count:l} from dependencies", - this.Pipeline, - "entity".ToQuantity(input.Count, "N0")); - - return input; - } - - private async Task RunPipeline(List input) - { - IEnumerable - output = await this.Pipeline.RunAsync(input); - - // Gather all the output. - this.logger.Debug( - "{Pipeline:l}: Gathering output", - this.Pipeline); - this.Outputs.Clear(); - this.Outputs.AddRange(output); - } - - private void SendToDependants() - { - if (this.Outgoing.Count <= 0) - { - return; - } - - // Make sure our internal wait for the consumers it set. - this.logger.Verbose( - "{Pipeline:l}: Setting up internal thread controls", - this.Pipeline); - this.waitingOnConsumers = this.Outgoing.Count; - this.consumersDone.Reset(); - - // Report how many files we're sending out and then use manual - // reset and the semaphore to control the threads. - this.logger.Debug( - "{Pipeline:l}: Output {Count:l} from pipeline", - this.Pipeline, - "entity".ToQuantity(this.Outputs.Count, "N0")); - - // Release our manual reset to allow operations to continue. - this.ChangeState(PipelineRunnerState.Providing); - this.logger.Verbose( - "{Pipeline:l}: Release manual reset for consumers", - this.Pipeline); - this.blockDependencies.Set(); - - // Wait until all consumers have finished processing. - this.consumersDone.Wait(); - } - - private void SignalDoneWithInputs() - { - if (this.Incoming.Count <= 0 || this.signaledDoneWithInputs) - { - return; - } - - this.signaledDoneWithInputs = true; - - this.logger.Debug( - "{Pipeline:l}: Signaling {Count:n0} dependencies done", - this.Pipeline, - this.Incoming.Count); - - foreach (var dependency in this.Incoming) - { - dependency.ConsumerDoneWithOutputs(); - } - } - - private bool WaitForDependencies() - { - if (this.Incoming.Count <= 0) - { - return false; - } - - // Wait for the dependencies to run first. - this.ChangeState(PipelineRunnerState.Waiting); - this.logger.Verbose( - "{Pipeline:l}: Waiting for {Count:l} to complete", - this.Pipeline, - "dependency".ToQuantity(this.Incoming.Count)); - - foreach (var dependency in this.Incoming) - { - dependency.WaitUntilProviding(); - } - - // Check for any error state in the dependency, if we have one, - // then we need to stop ourselves. - bool hasError = this.Incoming - .Any(x => x.State == PipelineRunnerState.Errored); - - if (!hasError) - { - return false; - } - - this.logger.Error( - "{Pipeline:l}: There was an exception in an dependency", - this.Pipeline); - this.ChangeState(PipelineRunnerState.Errored); - this.blockDependencies.Set(); - - return true; + this.consumersDone.Set(); } } + + /// + /// Initializes the runner after all external properties have been + /// set and configured. + /// + public void Initialize() + { + this.ChangeState(PipelineRunnerState.Initialized); + } + + /// + /// Executes the pipeline, including waiting for any or all + /// dependencies. + /// + public async Task RunAsync() + { + try + { + // Make sure we have a valid state. + switch (this.State) + { + case PipelineRunnerState.Initialized: + case PipelineRunnerState.Finalized: + break; + + default: + this.logger.Error( + "{Pipeline:l}: Pipeline cannot be started in a {State}" + + " state (not Initialized or Finalized)", + this.Pipeline, + this.State); + break; + } + + // Prepare ourselves for running. We have a start/stop state because + // this may be non-zero time. + this.started = DateTime.Now; + this.changed = DateTime.Now; + this.ChangeState(PipelineRunnerState.Preparing); + this.signaledDoneWithInputs = false; + this.ChangeState(PipelineRunnerState.Prepared); + + // Go through the incoming and wait for each of the manual resets + // on the dependency pipelines. + if (this.WaitForDependencies()) + { + this.SignalDoneWithInputs(); + return; + } + + // Grab the outputs from the incoming. They will be populated + // because we have waited for the reset events. + this.ChangeState(PipelineRunnerState.Started); + List input = this.GatherDependencyOutputs(); + + // Run the pipeline. This may not be resolved until we gather + // the output below. + await this.RunPipeline(input); + + // At this point, we are completely done with our inputs, so signal + // to them in case they have to clean up any of their structures. + this.SignalDoneWithInputs(); + + // If we have outgoing runners, provide them data until they are + // done. + this.SendToDependants(); + + // Finalize ourselves. + this.ChangeState(PipelineRunnerState.Finalized); + } + catch (Exception exception) + { + // Report the exception. + this.logger.Error(exception, "{Pipeline:l}: There was an exception running pipeline", this.Pipeline); + + // Change our state and then release any pipeline waiting for us + // so they can pick up the error and fail themselves. + this.ChangeState(PipelineRunnerState.Errored); + this.blockDependencies.Set(); + this.SignalDoneWithInputs(); + } + } + + /// + /// A method to block the call until this runner is done processing and + /// is ready to provide output. + /// + public void WaitUntilProviding() + { + this.blockDependencies.Wait(); + } + + /// + /// Changes the state of the pipeline into a new state. + /// + /// The state to change the pipeline into. + private void ChangeState(PipelineRunnerState newState) + { + this.logger.Verbose( + "{Pipeline:l}: Switching from state {Old} to {New} (elapsed {Elapsed}, duration {Duration})", + this.Pipeline, + this.State, + newState, + this.ElapsedFromInitialized, + this.ElapsedFromState); + this.changed = DateTime.Now; + this.State = newState; + } + + private List GatherDependencyOutputs() + { + if (this.Incoming.Count <= 0) + { + return new List(); + } + + // Report that we are gathering our outputs. + this.logger.Verbose( + "{Pipeline:l}: Gathering outputs from {Count:n0} dependencies", + this.Pipeline, + this.Incoming.Count); + + var input = this.Incoming.SelectMany(x => x.Outputs).ToList(); + + this.logger.Debug( + "{Pipeline:l}: Got {Count:l} from dependencies", + this.Pipeline, + "entity".ToQuantity(input.Count, "N0")); + + return input; + } + + private async Task RunPipeline(List input) + { + IEnumerable output = await this.Pipeline.RunAsync(input); + + // Gather all the output. + this.logger.Debug("{Pipeline:l}: Gathering output", this.Pipeline); + this.Outputs.Clear(); + this.Outputs.AddRange(output); + } + + private void SendToDependants() + { + if (this.Outgoing.Count <= 0) + { + return; + } + + // Make sure our internal wait for the consumers it set. + this.logger.Verbose("{Pipeline:l}: Setting up internal thread controls", this.Pipeline); + this.waitingOnConsumers = this.Outgoing.Count; + this.consumersDone.Reset(); + + // Report how many files we're sending out and then use manual + // reset and the semaphore to control the threads. + this.logger.Debug( + "{Pipeline:l}: Output {Count:l} from pipeline", + this.Pipeline, + "entity".ToQuantity(this.Outputs.Count, "N0")); + + // Release our manual reset to allow operations to continue. + this.ChangeState(PipelineRunnerState.Providing); + this.logger.Verbose("{Pipeline:l}: Release manual reset for consumers", this.Pipeline); + this.blockDependencies.Set(); + + // Wait until all consumers have finished processing. + this.consumersDone.Wait(); + } + + private void SignalDoneWithInputs() + { + if (this.Incoming.Count <= 0 || this.signaledDoneWithInputs) + { + return; + } + + this.signaledDoneWithInputs = true; + + this.logger.Debug("{Pipeline:l}: Signaling {Count:n0} dependencies done", this.Pipeline, this.Incoming.Count); + + foreach (PipelineRunner? dependency in this.Incoming) + { + dependency.ConsumerDoneWithOutputs(); + } + } + + private bool WaitForDependencies() + { + if (this.Incoming.Count <= 0) + { + return false; + } + + // Wait for the dependencies to run first. + this.ChangeState(PipelineRunnerState.Waiting); + this.logger.Verbose( + "{Pipeline:l}: Waiting for {Count:l} to complete", + this.Pipeline, + "dependency".ToQuantity(this.Incoming.Count)); + + foreach (PipelineRunner? dependency in this.Incoming) + { + dependency.WaitUntilProviding(); + } + + // Check for any error state in the dependency, if we have one, + // then we need to stop ourselves. + bool hasError = this.Incoming.Any(x => x.State == PipelineRunnerState.Errored); + + if (!hasError) + { + return false; + } + + this.logger.Error("{Pipeline:l}: There was an exception in an dependency", this.Pipeline); + this.ChangeState(PipelineRunnerState.Errored); + this.blockDependencies.Set(); + + return true; + } } diff --git a/src/Nitride/Pipelines/PipelineRunnerState.cs b/src/Nitride/Pipelines/PipelineRunnerState.cs index 3d67e03..d395edc 100644 --- a/src/Nitride/Pipelines/PipelineRunnerState.cs +++ b/src/Nitride/Pipelines/PipelineRunnerState.cs @@ -1,72 +1,71 @@ -namespace Nitride.Pipelines +namespace Nitride.Pipelines; + +/// +/// Describes the state of the pipelines which is used both for reporting +/// purposes but also to allow the pipelines to respond to changes in their +/// own state, such as unloading output elements. +/// +public enum PipelineRunnerState { /// - /// Describes the state of the pipelines which is used both for reporting - /// purposes but also to allow the pipelines to respond to changes in their - /// own state, such as unloading output elements. + /// Indicates that the runner is setting up and not ready for any use. /// - public enum PipelineRunnerState - { - /// - /// Indicates that the runner is setting up and not ready for any use. - /// - Initializing, + Initializing, - /// - /// Indicates that the runner has been initialized. - /// - Initialized, + /// + /// Indicates that the runner has been initialized. + /// + Initialized, - /// - /// Indicates that the pipeline is prepare for a new run. This is done - /// when the system determines it needs to run. - /// - Preparing, + /// + /// Indicates that the pipeline is prepare for a new run. This is done + /// when the system determines it needs to run. + /// + Preparing, - /// - /// Indicates that the pipeline has finished preparing. - /// - Prepared, + /// + /// Indicates that the pipeline has finished preparing. + /// + Prepared, - /// - /// Indicates that the runner is waiting for dependencies to run. - /// - Waiting, + /// + /// Indicates that the runner is waiting for dependencies to run. + /// + Waiting, - /// - /// Indicates that the pipeline has started processing by a call to - /// `RunAsync`. - /// - Started, + /// + /// Indicates that the pipeline has started processing by a call to + /// `RunAsync`. + /// + Started, - /// - /// Indicates that the pipeline is providing data to any dependencies. - /// If the pipeline has no dependencies, then this will never be called. - /// - Providing, + /// + /// Indicates that the pipeline is providing data to any dependencies. + /// If the pipeline has no dependencies, then this will never be called. + /// + Providing, - /// - /// Indicates that all the dependencies on this pipeline has finished - /// running (state `Finished`) and the pipeline can clean up any - /// memory elements. - /// - Provided, + /// + /// Indicates that all the dependencies on this pipeline has finished + /// running (state `Finished`) and the pipeline can clean up any + /// memory elements. + /// + Provided, - /// - /// Indicates that the pipeline is done running and the system is - /// shutting down. - /// - Finalizing, + /// + /// Indicates that the pipeline is done running and the system is + /// shutting down. + /// + Finalizing, - /// - /// Indicates that the pipeline has been completely cleaned up and - /// finished. It will never be used again. - /// - Finalized, + /// + /// Indicates that the pipeline has been completely cleaned up and + /// finished. It will never be used again. + /// + Finalized, - /// - /// Indicates that there was an error while running the pipeline. - /// - Errored, - } + /// + /// Indicates that there was an error while running the pipeline. + /// + Errored, } diff --git a/src/Nitride/Pipelines/README.md b/src/Nitride/Pipelines/README.md index 9b3ba7a..b05e437 100644 --- a/src/Nitride/Pipelines/README.md +++ b/src/Nitride/Pipelines/README.md @@ -51,7 +51,7 @@ ContainerBuilder builder; builder.RegisterInstance().AsSelf(); builder.RegisterInstance().AsSelf(); -IContainer container = builder.Build(); +IContainer container = builder; var loadPipeline = new Pipeline(); var geminiPipeline = container.Resolve(); diff --git a/src/Nitride/ROADMAP.md b/src/Nitride/ROADMAP.md index b5b2761..0d8e157 100644 --- a/src/Nitride/ROADMAP.md +++ b/src/Nitride/ROADMAP.md @@ -2,8 +2,8 @@ ## Immediate -- Switch the various operations to be async - - ReadFiles - - WriteFiles -- Implement mime type determination -- Implement a convert to text content based on mime type +- Switch the various operations to be async + - ReadFiles + - WriteFiles +- Implement mime type determination +- Implement a convert to text content based on mime type diff --git a/src/Nitride/Registration/NoRegistrationAttribute.cs b/src/Nitride/Registration/NoRegistrationAttribute.cs deleted file mode 100644 index 63b1b9f..0000000 --- a/src/Nitride/Registration/NoRegistrationAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Nitride.Registration -{ - [AttributeUsage(AttributeTargets.Class)] - public class NoRegistrationAttribute : Attribute - { - } -} diff --git a/src/Nitride/Registration/SingletonAttribute.cs b/src/Nitride/Registration/SingletonAttribute.cs deleted file mode 100644 index 46612be..0000000 --- a/src/Nitride/Registration/SingletonAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Nitride.Registration -{ - [AttributeUsage(AttributeTargets.Class)] - public class SingletonAttribute : Attribute - { - } -} diff --git a/src/Nitride/WithPropertiesAttribute.cs b/src/Nitride/WithPropertiesAttribute.cs index ea7a325..f4d011f 100644 --- a/src/Nitride/WithPropertiesAttribute.cs +++ b/src/Nitride/WithPropertiesAttribute.cs @@ -1,14 +1,13 @@ using System; -namespace Nitride +namespace Nitride; + +/// +/// A marker attribute that indicates that the source generator should +/// automatically add `Set*` methods for every public property in the class. +/// The class must be `partial` for this to work. +/// +[AttributeUsage(AttributeTargets.Class)] +public class WithPropertiesAttribute : Attribute { - /// - /// A marker attribute that indicates that the source generator should - /// automatically add `Set*` methods for every public property in the class. - /// The class must be `partial` for this to work. - /// - [AttributeUsage(AttributeTargets.Class)] - public class WithPropertiesAttribute : Attribute - { - } } diff --git a/tests/Nitride.IO.Tests/AddPathPrefixTest.cs b/tests/Nitride.IO.Tests/AddPathPrefixTest.cs new file mode 100644 index 0000000..575f03a --- /dev/null +++ b/tests/Nitride.IO.Tests/AddPathPrefixTest.cs @@ -0,0 +1,52 @@ +using System.Linq; + +using Nitride.IO.Contents; +using Nitride.IO.Paths; + +using Xunit; +using Xunit.Abstractions; + +using Zio; + +namespace Nitride.IO.Tests; + +public class AddPathPrefixTest : NitrideIOTestBase +{ + public AddPathPrefixTest(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void PrefixAllFiles() + { + // Set up the test. + using NitrideIOTestContext context = this.CreateContext(); + + // Set up the file. + IFileSystem fileSystem = context.FileSystem; + + fileSystem.CreateFile("/b1.txt"); + fileSystem.CreateFile("/c1.md"); + + // Set up the operation. + ReadFiles readFiles = context.Resolve(); + AddPathPrefix op = context.Resolve().WithPathPrefix("/prefix"); + + // Read and replace the paths. + IOrderedEnumerable output = readFiles.WithPattern("/**") + .Run() + .Run(op) + .Select(x => x.Get().ToString()) + .OrderBy(x => x); + + // Verify the results. + Assert.Equal( + new[] + { + "/prefix/b1.txt", + "/prefix/c1.md", + }, + output); + } +} diff --git a/tests/Nitride.IO.Tests/AddPathPrefixTests.cs b/tests/Nitride.IO.Tests/AddPathPrefixTests.cs deleted file mode 100644 index 4f6b36c..0000000 --- a/tests/Nitride.IO.Tests/AddPathPrefixTests.cs +++ /dev/null @@ -1,48 +0,0 @@ -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(); - var op = new AddPathPrefix("/prefix"); - - // Read and replace the paths. - IOrderedEnumerable output = readFiles(this.fileSystem) - .Read() - .Run(op) - .Select(x => x.Get().ToString()) - .OrderBy(x => x); - - // Verify the results. - Assert.Equal( - new[] - { - "/prefix/b1.txt", - "/prefix/c1.md", - }, - output); - } - } -} diff --git a/tests/Nitride.IO.Tests/MoveToIndexPathsTest.cs b/tests/Nitride.IO.Tests/MoveToIndexPathsTest.cs new file mode 100644 index 0000000..9ed4e4b --- /dev/null +++ b/tests/Nitride.IO.Tests/MoveToIndexPathsTest.cs @@ -0,0 +1,94 @@ +using System.Linq; + +using Nitride.IO.Contents; +using Nitride.IO.Paths; + +using Xunit; +using Xunit.Abstractions; + +using Zio; + +namespace Nitride.IO.Tests; + +public class MoveToIndexPathsTest : NitrideIOTestBase +{ + public MoveToIndexPathsTest(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void MoveAllFiles() + { + // Set up the test and file system. + using NitrideIOTestContext context = this.CreateContext(); + + CreateFileSystem(context); + + // Set up the operation. + ReadFiles readFiles = context.Resolve(); + MoveToIndexPath op = context.Resolve(); + + // Read and replace the paths. + IOrderedEnumerable output = readFiles.WithPattern("/**") + .Run() + .Run(op) + .Select(x => x.Get().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 test and file system. + using NitrideIOTestContext context = this.CreateContext(); + + CreateFileSystem(context); + + // Set up the operation. + ReadFiles readFiles = context.Resolve(); + MoveToIndexPath op = context.Resolve() + .WithCanMoveCallback(path => path.ToString().Contains("a1")); + + // Read and replace the paths. + IOrderedEnumerable output = readFiles.WithPattern("/**") + .Run() + .Run(op) + .Select(x => x.Get().ToString()) + .OrderBy(x => x); + + // Verify the results. + Assert.Equal( + new[] + { + "/a1/index", + "/b1.txt", + "/c1/index.md", + "/d1.html", + }, + output); + } + + private static void CreateFileSystem(NitrideIOTestContext context) + { + // Set up the file system. + IFileSystem fileSystem = context.FileSystem; + + fileSystem.CreateDirectory("/c1"); + fileSystem.CreateFile("/a1"); + fileSystem.CreateFile("/b1.txt"); + fileSystem.CreateFile("/c1/index.md"); + fileSystem.CreateFile("/d1.html"); + } +} diff --git a/tests/Nitride.IO.Tests/MoveToIndexPathsTests.cs b/tests/Nitride.IO.Tests/MoveToIndexPathsTests.cs deleted file mode 100644 index 77e8831..0000000 --- a/tests/Nitride.IO.Tests/MoveToIndexPathsTests.cs +++ /dev/null @@ -1,80 +0,0 @@ -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(); - var op = new MoveToIndexPaths(); - - // Read and replace the paths. - IOrderedEnumerable output = readFiles(this.fileSystem) - .Read() - .Run(op) - .Select(x => x.Get().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(); - MoveToIndexPaths? op = new MoveToIndexPaths() - .WithCanMoveCallback((path) => path.ToString().Contains("a1")); - - // Read and replace the paths. - IOrderedEnumerable output = readFiles(this.fileSystem) - .Read() - .Run(op) - .Select(x => x.Get().ToString()) - .OrderBy(x => x); - - // Verify the results. - Assert.Equal( - new[] - { - "/a1/index", - "/b1.txt", - "/c1/index.md", - "/d1.html", - }, - output); - } - } -} diff --git a/tests/Nitride.IO.Tests/Nitride.IO.Tests.csproj b/tests/Nitride.IO.Tests/Nitride.IO.Tests.csproj index aeedbcf..fe780a0 100644 --- a/tests/Nitride.IO.Tests/Nitride.IO.Tests.csproj +++ b/tests/Nitride.IO.Tests/Nitride.IO.Tests.csproj @@ -1,29 +1,29 @@ - - net5.0 - enable - + + net6.0 + enable + - - - - + + + + - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/tests/Nitride.IO.Tests/NitrideIOTestBase.cs b/tests/Nitride.IO.Tests/NitrideIOTestBase.cs new file mode 100644 index 0000000..13031ff --- /dev/null +++ b/tests/Nitride.IO.Tests/NitrideIOTestBase.cs @@ -0,0 +1,13 @@ +using MfGames.TestSetup; + +using Xunit.Abstractions; + +namespace Nitride.IO.Tests; + +public abstract class NitrideIOTestBase : TestBase +{ + protected NitrideIOTestBase(ITestOutputHelper output) + : base(output) + { + } +} diff --git a/tests/Nitride.IO.Tests/NitrideIOTestContext.cs b/tests/Nitride.IO.Tests/NitrideIOTestContext.cs new file mode 100644 index 0000000..d3d125a --- /dev/null +++ b/tests/Nitride.IO.Tests/NitrideIOTestContext.cs @@ -0,0 +1,34 @@ +using Autofac; + +using Nitride.Tests; + +using Serilog; + +using Zio; +using Zio.FileSystems; + +namespace Nitride.IO.Tests; + +public class NitrideIOTestContext : NitrideTestContext +{ + private static int bob = 0; + + public IFileSystem FileSystem => this.Resolve(); + + /// + protected override void ConfigureContainer(ContainerBuilder builder) + { + base.ConfigureContainer(builder); + builder.RegisterModule(); + + builder.RegisterInstance(new MemoryFileSystem()).As().SingleInstance(); + + builder.RegisterBuildCallback(x => x.Resolve().Error("Registered!")); + builder.RegisterInstance(new Bob { Value = bob++ }).As().SingleInstance(); + } + + public class Bob + { + public int Value { get; set; } + } +} diff --git a/tests/Nitride.IO.Tests/NitrideIOTestsBase.cs b/tests/Nitride.IO.Tests/NitrideIOTestsBase.cs deleted file mode 100644 index e93ccfd..0000000 --- a/tests/Nitride.IO.Tests/NitrideIOTestsBase.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Autofac; -using Nitride.Tests; -using Xunit.Abstractions; - -namespace Nitride.IO.Tests -{ - public abstract class NitrideIOTestsBase : NitrideTestsBase - { - protected NitrideIOTestsBase(ITestOutputHelper output) - : base(output) - { - } - - /// - protected override void ConfigureContainer(ContainerBuilder builder) - { - base.ConfigureContainer(builder); - builder.RegisterModule(); - } - } -} diff --git a/tests/Nitride.IO.Tests/ReadFilesTest.cs b/tests/Nitride.IO.Tests/ReadFilesTest.cs new file mode 100644 index 0000000..bc36080 --- /dev/null +++ b/tests/Nitride.IO.Tests/ReadFilesTest.cs @@ -0,0 +1,109 @@ +using System.Linq; + +using Nitride.IO.Contents; + +using Xunit; +using Xunit.Abstractions; + +using Zio; + +namespace Nitride.IO.Tests; + +/// +/// Tests the functionality of the ReadFiles(). +/// +public class ReadFilesTest : NitrideIOTestBase +{ + public ReadFilesTest(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void ReadAllFiles() + { + // Set up the test and file system. + using NitrideIOTestContext context = this.CreateContext(); + + CreateFileSystem(context); + + // Set up the operation. + ReadFiles op = context.Resolve(); + + // Verify the paths. + IOrderedEnumerable paths = op.WithPattern("/**") + .Run() + .Select(x => x.Get()) + .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 test and file system. + using NitrideIOTestContext context = this.CreateContext(); + + CreateFileSystem(context); + + // Set up the operation. + ReadFiles op = context.Resolve().WithPattern("/*.txt"); + + // Verify the paths. + IOrderedEnumerable paths = op.Run().Select(x => (string)x.Get()).OrderBy(x => x); + + Assert.Equal( + new[] + { + "/a.txt", + }, + paths); + } + + [Fact] + public void ReadGlobWithSubdirectories() + { + // Set up the test and file system. + using NitrideIOTestContext context = this.CreateContext(); + + CreateFileSystem(context); + + // Set up the operation. + ReadFiles op = context.Resolve().WithPattern("**/*.txt"); + + // Verify the paths. + IOrderedEnumerable paths = op.Run().Select(x => (string)x.Get()).OrderBy(x => x); + + Assert.Equal( + new[] + { + "/a.txt", + "/c1/c.txt", + }, + paths); + } + + private static void CreateFileSystem(NitrideIOTestContext context) + { + IFileSystem fileSystem = context.FileSystem; + + fileSystem.CreateDirectory("/b1"); + fileSystem.CreateDirectory("/c1"); + fileSystem.CreateDirectory("/c1/c2"); + fileSystem.CreateDirectory("/d1"); + fileSystem.WriteAllText("/a.txt", "File A"); + fileSystem.WriteAllText("/b1/b.md", "File B"); + fileSystem.WriteAllText("/c1/c.txt", "File C"); + fileSystem.WriteAllText("/c1/c2/e.md", "File C"); + } +} diff --git a/tests/Nitride.IO.Tests/ReadFilesTests.cs b/tests/Nitride.IO.Tests/ReadFilesTests.cs deleted file mode 100644 index 1e84103..0000000 --- a/tests/Nitride.IO.Tests/ReadFilesTests.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System.Linq; -using Autofac; -using Nitride.IO.Contents; -using Xunit; -using Xunit.Abstractions; -using Zio; -using Zio.FileSystems; - -namespace Nitride.IO.Tests -{ - /// - /// Tests the functionality of the ReadFiles(). - /// - 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 op = factory(this.fileSystem); - - // Verify the paths. - IOrderedEnumerable paths = op.Read() - .Select(x => x.Get()) - .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 op = factory(this.fileSystem); - - // Verify the paths. - IOrderedEnumerable paths = op.Read("/*.txt") - .Select(x => (string)x.Get()) - .OrderBy(x => x); - - Assert.Equal( - new[] - { - "/a.txt", - }, - paths); - } - - [Fact] - public void ReadGlobWithSubdirectories() - { - // Set up the operation. - var factory = this.Container.Resolve(); - ReadFiles op = factory(this.fileSystem); - - // Verify the paths. - IOrderedEnumerable paths = op.Read("**/*.txt") - .Select(x => (string)x.Get()) - .OrderBy(x => x); - - Assert.Equal( - new[] - { - "/a.txt", - "/c1/c.txt", - }, - paths); - } - } -} diff --git a/tests/Nitride.IO.Tests/RemovePathPrefixTest.cs b/tests/Nitride.IO.Tests/RemovePathPrefixTest.cs new file mode 100644 index 0000000..423f735 --- /dev/null +++ b/tests/Nitride.IO.Tests/RemovePathPrefixTest.cs @@ -0,0 +1,54 @@ +using System.Linq; + +using Nitride.IO.Contents; +using Nitride.IO.Paths; + +using Xunit; +using Xunit.Abstractions; + +using Zio; + +namespace Nitride.IO.Tests; + +public class RemovePathPrefixTest : NitrideIOTestBase +{ + public RemovePathPrefixTest(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void PrefixAllFiles() + { + // Set up the test. + using NitrideIOTestContext context = this.CreateContext(); + + // Set up the file. + IFileSystem fileSystem = context.FileSystem; + + fileSystem.CreateDirectory("/a"); + fileSystem.CreateDirectory("/a/a"); + fileSystem.CreateFile("/a/b1.txt"); + fileSystem.CreateFile("/a/a/c1.md"); + + // Set up the operation. + ReadFiles readFiles = context.Resolve(); + RemovePathPrefix op = context.Resolve().WithPathPrefix("/a"); + + // Read and replace the paths. + IOrderedEnumerable output = readFiles.WithPattern("/**") + .Run() + .Run(op) + .Select(x => x.Get().ToString()) + .OrderBy(x => x); + + // Verify the results. + Assert.Equal( + new[] + { + "/a/c1.md", + "/b1.txt", + }, + output); + } +} diff --git a/tests/Nitride.IO.Tests/RemovePathPrefixTests.cs b/tests/Nitride.IO.Tests/RemovePathPrefixTests.cs deleted file mode 100644 index 99270bc..0000000 --- a/tests/Nitride.IO.Tests/RemovePathPrefixTests.cs +++ /dev/null @@ -1,50 +0,0 @@ -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(); - var op = new RemovePathPrefix("/a"); - - // Read and replace the paths. - IOrderedEnumerable output = readFiles(this.fileSystem) - .Read() - .Run(op) - .Select(x => x.Get().ToString()) - .OrderBy(x => x); - - // Verify the results. - Assert.Equal( - new[] - { - "/a/c1.md", - "/b1.txt", - }, - output); - } - } -} diff --git a/tests/Nitride.IO.Tests/WriteFilesTest.cs b/tests/Nitride.IO.Tests/WriteFilesTest.cs index 5c9aacf..d959ba4 100644 --- a/tests/Nitride.IO.Tests/WriteFilesTest.cs +++ b/tests/Nitride.IO.Tests/WriteFilesTest.cs @@ -1,63 +1,65 @@ 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 +namespace Nitride.IO.Tests; + +/// +/// Tests the functionality of the WriteFiles(). +/// +public class WriteFilesTest : NitrideIOTestBase { - /// - /// Tests the functionality of the WriteFiles(). - /// - public class WriteFilesTest : NitrideIOTestsBase + public WriteFilesTest(ITestOutputHelper output) + : base(output) { - 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 test. + using NitrideIOTestContext context = this.CreateContext(); - [Fact] - public void WriteAllFiles() - { - // Set up the operation. - var output = new MemoryFileSystem(); - var readFiles = this.Container.Resolve(); - var factory = this.Container.Resolve(); - WriteFiles op = factory(output); + // Set up the file. + IFileSystem fileSystem = context.FileSystem; - // 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() == "/b1/b.md" - ? x.SetTextContent( - ((ITextContentConvertable)x.GetBinaryContent()) - .ToTextContent()) - : x) - .Run(op); + fileSystem.CreateDirectory("/b1"); + fileSystem.CreateDirectory("/c1"); + fileSystem.CreateDirectory("/c1/c2"); + fileSystem.CreateDirectory("/d1"); + fileSystem.WriteAllText("/a.txt", "File A"); + fileSystem.WriteAllText("/b1/b.md", "File B"); + fileSystem.WriteAllText("/c1/c2/e.md", "File E"); - // Verify the results. - Assert.True(output.FileExists("/a.txt")); - Assert.True(output.FileExists("/b1/b.md")); - Assert.True(output.FileExists("/c1/c2/e.md")); + // Set up the operation. + var output = new MemoryFileSystem(); + ReadFiles readFiles = context.Resolve(); + WriteFiles op = context.Resolve().WithFileSystem(output); - 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")); - } + // Read and write out the files. We switch one of the files to be + // text content to make sure that works too. + readFiles.WithPattern("/**") + .Run() + .Select( + x => x.Get() == "/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")); } } diff --git a/tests/Nitride.Tests/EntityContentTest.cs b/tests/Nitride.Tests/EntityContentTest.cs new file mode 100644 index 0000000..791ad98 --- /dev/null +++ b/tests/Nitride.Tests/EntityContentTest.cs @@ -0,0 +1,70 @@ +using Gallium; + +using Nitride.Contents; + +using Xunit; +using Xunit.Abstractions; + +namespace Nitride.Tests; + +/// +/// Tests the various functionality of the high-level content methods and +/// extension methods. +/// +public class EntityContentTest : NitrideTestBase +{ + public EntityContentTest(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void AddBinaryContent() + { + var content = new ByteArrayBinaryContent(new byte[0]); + Entity entity = new Entity().SetBinaryContent(content); + + Assert.Equal(1, entity.Count); + Assert.True(entity.HasContent()); + Assert.True(entity.HasBinaryContent()); + Assert.False(entity.HasTextContent()); + } + + [Fact] + public void AddTextContent() + { + var content = new StringTextContent("contents"); + Entity entity = new Entity().SetTextContent(content); + + Assert.Equal(1, entity.Count); + Assert.True(entity.HasContent()); + Assert.False(entity.HasBinaryContent()); + Assert.True(entity.HasTextContent()); + } + + [Fact] + public void SwitchBinaryToTextContent() + { + var binaryContent = new ByteArrayBinaryContent(new byte[0]); + var textContent = new StringTextContent("contents"); + Entity entity = new Entity().SetBinaryContent(binaryContent).SetTextContent(textContent); + + Assert.Equal(1, entity.Count); + Assert.True(entity.HasContent()); + Assert.False(entity.HasBinaryContent()); + Assert.True(entity.HasTextContent()); + } + + [Fact] + public void SwitchTextToBinaryContent() + { + var binaryContent = new ByteArrayBinaryContent(new byte[0]); + var textContent = new StringTextContent("contents"); + Entity entity = new Entity().SetContent(textContent).SetContent(binaryContent); + + Assert.Equal(1, entity.Count); + Assert.True(entity.HasContent()); + Assert.True(entity.HasBinaryContent()); + Assert.False(entity.HasTextContent()); + } +} diff --git a/tests/Nitride.Tests/EntityContentTests.cs b/tests/Nitride.Tests/EntityContentTests.cs deleted file mode 100644 index 0cc94af..0000000 --- a/tests/Nitride.Tests/EntityContentTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -using Gallium; -using Nitride.Contents; -using Xunit; -using Xunit.Abstractions; - -namespace Nitride.Tests -{ - /// - /// Tests the various functionality of the high-level content methods and - /// extension methods. - /// - public class EntityContentTests : NitrideTestsBase - { - public EntityContentTests(ITestOutputHelper output) - : base(output) - { - } - - [Fact] - public void AddBinaryContent() - { - var content = new ByteArrayBinaryContent(new byte[0]); - Entity entity = new Entity() - .SetBinaryContent(content); - - Assert.Equal(1, entity.Count); - Assert.True(entity.HasContent()); - Assert.True(entity.HasBinaryContent()); - Assert.False(entity.HasTextContent()); - } - - [Fact] - public void AddTextContent() - { - var content = new StringTextContent("contents"); - Entity entity = new Entity() - .SetTextContent(content); - - Assert.Equal(1, entity.Count); - Assert.True(entity.HasContent()); - Assert.False(entity.HasBinaryContent()); - Assert.True(entity.HasTextContent()); - } - - [Fact] - public void SwitchBinaryToTextContent() - { - var binaryContent = new ByteArrayBinaryContent(new byte[0]); - var textContent = new StringTextContent("contents"); - Entity entity = new Entity() - .SetBinaryContent(binaryContent) - .SetTextContent(textContent); - - Assert.Equal(1, entity.Count); - Assert.True(entity.HasContent()); - Assert.False(entity.HasBinaryContent()); - Assert.True(entity.HasTextContent()); - } - - [Fact] - public void SwitchTextToBinaryContent() - { - var binaryContent = new ByteArrayBinaryContent(new byte[0]); - var textContent = new StringTextContent("contents"); - Entity entity = new Entity() - .SetContent(textContent) - .SetContent(binaryContent); - - Assert.Equal(1, entity.Count); - Assert.True(entity.HasContent()); - Assert.True(entity.HasBinaryContent()); - Assert.False(entity.HasTextContent()); - } - } -} diff --git a/tests/Nitride.Tests/Nitride.Tests.csproj b/tests/Nitride.Tests/Nitride.Tests.csproj index 82619eb..8ed663e 100644 --- a/tests/Nitride.Tests/Nitride.Tests.csproj +++ b/tests/Nitride.Tests/Nitride.Tests.csproj @@ -1,30 +1,31 @@ - - net5.0 - false - enable - Nitride.Tests - + + net6.0 + false + enable + Nitride.Tests + - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + - - - + + + diff --git a/tests/Nitride.Tests/NitrideTestBase.cs b/tests/Nitride.Tests/NitrideTestBase.cs new file mode 100644 index 0000000..d3d4c9b --- /dev/null +++ b/tests/Nitride.Tests/NitrideTestBase.cs @@ -0,0 +1,17 @@ +using MfGames.TestSetup; + +using Xunit.Abstractions; + +namespace Nitride.Tests; + +/// +/// Common initialization logic for Nitride-based tests including setting +/// up containers, logging, and Serilog. +/// +public abstract class NitrideTestBase : TestBase +{ + protected NitrideTestBase(ITestOutputHelper output) + : base(output) + { + } +} diff --git a/tests/Nitride.Tests/NitrideTestContext.cs b/tests/Nitride.Tests/NitrideTestContext.cs new file mode 100644 index 0000000..6fb9d36 --- /dev/null +++ b/tests/Nitride.Tests/NitrideTestContext.cs @@ -0,0 +1,18 @@ +using Autofac; + +using MfGames.TestSetup; + +namespace Nitride.Tests; + +/// +/// The base test context for Nitride tests. +/// +public class NitrideTestContext : TestContext +{ + /// + protected override void ConfigureContainer(ContainerBuilder builder) + { + base.ConfigureContainer(builder); + builder.RegisterModule(); + } +} diff --git a/tests/Nitride.Tests/NitrideTestsBase.cs b/tests/Nitride.Tests/NitrideTestsBase.cs deleted file mode 100644 index 1cf3ada..0000000 --- a/tests/Nitride.Tests/NitrideTestsBase.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Autofac; -using Serilog; -using Serilog.Core; -using Xunit.Abstractions; - -namespace Nitride.Tests -{ - /// - /// Common initialization logic for Nitride-based tests including setting - /// up containers, logging, and Serilog. - /// - public abstract class NitrideTestsBase - { - private readonly ITestOutputHelper output; - - public NitrideTestsBase(ITestOutputHelper output) - { - this.output = output; - - // Set up logging. - const string Template = - "[{Level:u3}] " - + "({SourceContext}) {Message}" - + "{NewLine}{Exception}"; - - this.Logger = new LoggerConfiguration() - .WriteTo.TestOutput( - output, - outputTemplate: Template) - .CreateLogger(); - - // Set up Autofac. - var builder = new ContainerBuilder(); - - builder.RegisterInstance(this.Logger) - .As() - .SingleInstance(); - builder.RegisterModule(); - - this.ConfigureContainer(builder); - - this.Container = builder.Build(); - } - - /// - /// Contains the Autofac container for this test. - /// - protected IContainer Container { get; } - - /// - /// Gets the logger used to report messages about the test. - /// - protected Logger Logger { get; } - - /// - /// Configures the Autofac container for this test instance. - /// - /// - protected virtual void ConfigureContainer(ContainerBuilder builder) - { - } - } -} diff --git a/tests/Nitride.Yaml.Tests/Nitride.Yaml.Tests.csproj b/tests/Nitride.Yaml.Tests/Nitride.Yaml.Tests.csproj index 0c96bae..4c1423b 100644 --- a/tests/Nitride.Yaml.Tests/Nitride.Yaml.Tests.csproj +++ b/tests/Nitride.Yaml.Tests/Nitride.Yaml.Tests.csproj @@ -1,29 +1,29 @@ - - net5.0 - enable - + + net6.0 + enable + - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + - - - - - + + + + + diff --git a/tests/Nitride.Yaml.Tests/ParseYamlHeaderTest.cs b/tests/Nitride.Yaml.Tests/ParseYamlHeaderTest.cs new file mode 100644 index 0000000..2f962ec --- /dev/null +++ b/tests/Nitride.Yaml.Tests/ParseYamlHeaderTest.cs @@ -0,0 +1,96 @@ +using System.Collections.Generic; +using System.Linq; + +using Gallium; + +using Nitride.Contents; +using Nitride.Tests; + +using Xunit; +using Xunit.Abstractions; + +namespace Nitride.Yaml.Tests; + +public class ParseYamlHeaderTest : NitrideTestBase +{ + public ParseYamlHeaderTest(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void ParseNoContent() + { + var input = new List + { + new Entity().SetTextContent(new StringTextContent("---", "title: Test Title 1", "---")) + }; + + var op = new ParseYamlHeader(); + + Entity output = input.Run(op).First(); + + Assert.True(output.Has()); + Assert.Equal( + new[] + { + string.Empty, + }, + output.Get().GetText().Split("\n")); + Assert.True(output.Has()); + Assert.Equal("Test Title 1", output.Get().Title); + } + + [Fact] + public void ParseNoHeader() + { + var input = new List + { + new Entity().SetTextContent(new StringTextContent("Hello")) + }; + + var op = new ParseYamlHeader(); + + Entity output = input.Run(op).First(); + + Assert.True(output.Has()); + Assert.Equal( + new[] + { + "Hello", + string.Empty, + }, + output.Get().GetText().Split("\n")); + Assert.True(output.Has()); + Assert.Null(output.Get().Title); + } + + [Fact] + public void ParseSimpleHeader() + { + var input = new List + { + new Entity().SetTextContent(new StringTextContent("---", "title: Test Title 1", "---", "Hello")) + }; + + var op = new ParseYamlHeader(); + + Entity output = input.Run(op).First(); + + Assert.True(output.Has()); + Assert.Equal( + new[] + { + "Hello", + string.Empty, + }, + output.Get().GetText().Split("\n")); + Assert.True(output.Has()); + Assert.Equal("Test Title 1", output.Get().Title); + } + + private class TestHeader + { + public string? Title { get; set; } + } +} diff --git a/tests/Nitride.Yaml.Tests/ParseYamlHeaderTests.cs b/tests/Nitride.Yaml.Tests/ParseYamlHeaderTests.cs deleted file mode 100644 index 434513c..0000000 --- a/tests/Nitride.Yaml.Tests/ParseYamlHeaderTests.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Gallium; -using Nitride.Contents; -using Nitride.Tests; -using Xunit; -using Xunit.Abstractions; - -namespace Nitride.Yaml.Tests -{ - public class ParseYamlHeaderTests : NitrideTestsBase - { - public ParseYamlHeaderTests(ITestOutputHelper output) - : base(output) - { - } - - [Fact] - public void ParseNoContent() - { - var input = new List - { - new Entity() - .SetTextContent( - new StringTextContent( - "---", - "title: Test Title 1", - "---")) - }; - - var op = new ParseYamlHeader(); - - Entity output = input - .Run(op) - .First(); - - Assert.True(output.Has()); - Assert.Equal( - new[] - { - string.Empty, - }, - output.Get().GetText().Split("\n")); - Assert.True(output.Has()); - Assert.Equal("Test Title 1", output.Get().Title); - } - - [Fact] - public void ParseNoHeader() - { - var input = new List - { - new Entity() - .SetTextContent( - new StringTextContent( - "Hello")) - }; - - var op = new ParseYamlHeader(); - - Entity output = input - .Run(op) - .First(); - - Assert.True(output.Has()); - Assert.Equal( - new[] - { - "Hello", - string.Empty, - }, - output.Get().GetText().Split("\n")); - Assert.True(output.Has()); - Assert.Null(output.Get().Title); - } - - [Fact] - public void ParseSimpleHeader() - { - var input = new List - { - new Entity() - .SetTextContent( - new StringTextContent( - "---", - "title: Test Title 1", - "---", - "Hello")) - }; - - var op = new ParseYamlHeader(); - - Entity output = input - .Run(op) - .First(); - - Assert.True(output.Has()); - Assert.Equal( - new[] - { - "Hello", - string.Empty, - }, - output.Get().GetText().Split("\n")); - Assert.True(output.Has()); - Assert.Equal("Test Title 1", output.Get().Title); - } - - private class TestHeader - { - public string? Title { get; set; } - } - } -}