mirror of
https://github.com/nsnail/ns-ext.git
synced 2025-04-20 07:42:52 +08:00
Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c9975472bf | ||
![]() |
97c93bd854 | ||
![]() |
322cbc32df | ||
![]() |
fa84cc315b | ||
![]() |
d4a92aca6f | ||
![]() |
a9f3416c37 | ||
![]() |
890b229683 | ||
c3c6819a99 | |||
![]() |
efa6a564dc | ||
7c5b5443cb | |||
![]() |
689d9560a8 | ||
![]() |
49de51d649 | ||
6676323b26 | |||
![]() |
c7c978fe5c | ||
![]() |
892df78c26 | ||
![]() |
7a1c8db6e4 | ||
![]() |
2b6c7adff8 | ||
4c515ac127 | |||
![]() |
b32fe597dd | ||
![]() |
7557cb0538 | ||
44c30a2147 | |||
0e8319771e | |||
![]() |
32b7bdf700 | ||
41208a5d7f | |||
![]() |
301154a66c | ||
![]() |
bca2b60636 | ||
![]() |
3de9d3b8d0 | ||
![]() |
ddf07fce57 | ||
![]() |
00164b02d9 | ||
![]() |
246c666788 | ||
![]() |
cd779c8797 | ||
![]() |
51063bacfe | ||
![]() |
fa3aa0527e | ||
![]() |
a9cb90464d | ||
![]() |
879388409c | ||
![]() |
962e82dd9b | ||
![]() |
3e0f4a5693 | ||
![]() |
f15ea13782 | ||
![]() |
a286329fa5 | ||
![]() |
52d554347c | ||
![]() |
897df5838e | ||
![]() |
095a14c96b | ||
![]() |
2581d878ba | ||
![]() |
9c5d89c169 | ||
![]() |
acce7e0a74 | ||
![]() |
cc703816ca | ||
![]() |
cf2988b389 | ||
![]() |
8c0dbcf1f4 | ||
![]() |
de03df1318 |
72
.commitlintrc.js
Normal file
72
.commitlintrc.js
Normal file
@ -0,0 +1,72 @@
|
||||
// .commitlintrc.js
|
||||
/** @type {import('cz-git').UserConfig} */
|
||||
module.exports = {
|
||||
rules: {
|
||||
// @see: https://commitlint.js.org/#/reference-rules
|
||||
},
|
||||
prompt: {
|
||||
alias: { fd: 'docs: fix typos' },
|
||||
messages: {
|
||||
type: '选择你要提交的类型 :',
|
||||
scope: '选择一个提交范围(可选):',
|
||||
customScope: '请输入自定义的提交范围 :',
|
||||
subject: '填写简短精炼的变更描述 :\n',
|
||||
body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
|
||||
breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
|
||||
footerPrefixesSelect: '选择关联issue前缀(可选):',
|
||||
customFooterPrefix: '输入自定义issue前缀 :',
|
||||
footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
|
||||
confirmCommit: '是否提交或修改commit ?'
|
||||
},
|
||||
types: [
|
||||
{ value: 'wip', name: 'wip: 开发之中 | In development ', emoji:'🧠' },
|
||||
{ value: 'feat', name: 'feat: 新增功能 | A new feature', emoji:'✨' },
|
||||
{ value: 'fix', name: 'fix: 修复缺陷 | A bug fix', emoji:'🐛' },
|
||||
{ value: 'docs', name: 'docs: 文档更新 | Documentation only changes', emoji:'📝' },
|
||||
{ value: 'style', name: 'style: 代码格式 | Changes that do not affect the meaning of the code', emoji:'💄' },
|
||||
{ value: 'refactor', name: 'refactor: 代码重构 | A code change that neither fixes a bug nor adds a feature', emoji:'♻️' },
|
||||
{ value: 'perf', name: 'perf: 性能提升 | A code change that improves performance', emoji:'⚡' },
|
||||
{ value: 'test', name: 'test: 测试相关 | Adding missing tests or correcting existing tests', emoji:'✅' },
|
||||
{ value: 'build', name: 'build: 构建相关 | Changes that affect the build system or external dependencies', emoji:'📦' },
|
||||
{ value: 'ci', name: 'ci: 持续集成 | Changes to our CI configuration files and scripts', emoji:'🎡' },
|
||||
{ value: 'revert', name: 'revert: 回退代码 | Revert to a commit', emoji:'⏪' },
|
||||
{ value: 'chore', name: 'chore: 其他修改 | Other changes that do not modify src or test files', emoji:'🔨' },
|
||||
],
|
||||
useEmoji: true,
|
||||
emojiAlign: 'center',
|
||||
useAI: false,
|
||||
aiNumber: 1,
|
||||
themeColorCode: '',
|
||||
scopes: [],
|
||||
allowCustomScopes: true,
|
||||
allowEmptyScopes: true,
|
||||
customScopesAlign: 'bottom',
|
||||
customScopesAlias: 'custom',
|
||||
emptyScopesAlias: 'empty',
|
||||
upperCaseSubject: false,
|
||||
markBreakingChangeMode: false,
|
||||
allowBreakingChanges: ['feat', 'fix'],
|
||||
breaklineNumber: 100,
|
||||
breaklineChar: '|',
|
||||
skipQuestions: [],
|
||||
issuePrefixes: [
|
||||
// 如果使用 gitee 作为开发管理
|
||||
{ value: 'link', name: 'link: 链接 ISSUES 进行中' },
|
||||
{ value: 'closed', name: 'closed: 标记 ISSUES 已完成' }
|
||||
],
|
||||
customIssuePrefixAlign: 'top',
|
||||
emptyIssuePrefixAlias: 'skip',
|
||||
customIssuePrefixAlias: 'custom',
|
||||
allowCustomIssuePrefix: true,
|
||||
allowEmptyIssuePrefix: true,
|
||||
confirmColorize: true,
|
||||
maxHeaderLength: Infinity,
|
||||
maxSubjectLength: Infinity,
|
||||
minSubjectLength: 0,
|
||||
scopeOverrides: undefined,
|
||||
defaultBody: '[skip ci]',
|
||||
defaultIssues: '',
|
||||
defaultScope: '',
|
||||
defaultSubject: ''
|
||||
}
|
||||
}
|
108
.editorconfig
108
.editorconfig
@ -1,65 +1,63 @@
|
||||
# 此文件为 EditorConfig 配置文件,用于设置跨编辑器的代码格式化规则。
|
||||
# root = true 表示此文件是根配置文件。
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
ij_xml_attribute_wrap = off
|
||||
ij_xml_text_wrap = off
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = false
|
||||
trim_trailing_whitespace = true
|
||||
charset = utf-8 # 文件字符集为 UTF-8
|
||||
end_of_line = lf # 行结束符为 LF
|
||||
ij_xml_attribute_wrap = off # IntelliJ IDEA 中 XML 属性不换行
|
||||
ij_xml_text_wrap = off # IntelliJ IDEA 中 XML 文本不换行
|
||||
indent_size = 4 # 缩进大小为 4 个空格
|
||||
indent_style = space # 使用空格进行缩进
|
||||
insert_final_newline = false # 不在文件末尾插入空行
|
||||
max_line_length = 150 # 行长度限制为 150 个字符
|
||||
trim_trailing_whitespace = true # 删除行尾的空格
|
||||
|
||||
[{*.json,*.yml}]
|
||||
indent_size = 2 # 对于 JSON 和 YAML 文件,缩进大小为 2 个空格
|
||||
|
||||
[*.cs]
|
||||
dotnet_analyzer_diagnostic.severity = warning
|
||||
dotnet_diagnostic.CA1707.severity = none
|
||||
dotnet_diagnostic.CA1720.severity = none
|
||||
dotnet_diagnostic.CA5350.severity = none
|
||||
dotnet_diagnostic.CA5351.severity = none
|
||||
dotnet_diagnostic.IDE0008.severity = none
|
||||
dotnet_diagnostic.IDE0017.severity = none
|
||||
dotnet_diagnostic.IDE0048.severity = none
|
||||
dotnet_diagnostic.IDE0055.severity = none
|
||||
dotnet_diagnostic.IDE0058.severity = none
|
||||
dotnet_diagnostic.IDE0160.severity = none
|
||||
dotnet_analyzer_diagnostic.severity = warning # 设置 C# 文件中所有 dotnet_analyzer_diagnostic 的严重性级别为 warning
|
||||
|
||||
[*.g.cs]
|
||||
dotnet_analyzer_diagnostic.severity = none # 禁用所有代码分析规则
|
||||
|
||||
|
||||
# ReSharper properties
|
||||
resharper_align_linq_query = true
|
||||
resharper_align_multiline_argument = true
|
||||
resharper_align_multiline_array_and_object_initializer = true
|
||||
resharper_align_multiline_binary_patterns = true
|
||||
resharper_align_multiline_calls_chain = true
|
||||
resharper_align_multiline_extends_list = true
|
||||
resharper_align_multiline_parameter = true
|
||||
resharper_align_multiline_property_pattern = true
|
||||
resharper_align_multiline_switch_expression = true
|
||||
resharper_align_multiple_declaration = true
|
||||
resharper_align_multline_type_parameter_constrains = true
|
||||
resharper_align_multline_type_parameter_list = true
|
||||
resharper_align_tuple_components = true
|
||||
resharper_allow_comment_after_lbrace = true
|
||||
resharper_blank_lines_before_single_line_comment = 1
|
||||
resharper_csharp_empty_block_style = together_same_line
|
||||
resharper_csharp_outdent_commas = true
|
||||
resharper_csharp_place_type_constraints_on_same_line = false
|
||||
resharper_csharp_stick_comment = false
|
||||
resharper_csharp_wrap_before_comma = true
|
||||
resharper_indent_nested_for_stmt = true
|
||||
resharper_indent_nested_foreach_stmt = true
|
||||
resharper_indent_nested_while_stmt = true
|
||||
resharper_indent_preprocessor_if = usual_indent
|
||||
resharper_indent_preprocessor_other = usual_indent
|
||||
resharper_int_align = true
|
||||
resharper_keep_existing_arrangement = false
|
||||
resharper_place_linq_into_on_new_line = false
|
||||
resharper_place_simple_embedded_statement_on_same_line = false
|
||||
resharper_place_simple_switch_expression_on_single_line = true
|
||||
resharper_wrap_before_eq = true
|
||||
resharper_wrap_chained_method_calls = chop_if_long
|
||||
resharper_wrap_switch_expression = chop_if_long
|
||||
|
||||
resharper_align_linq_query = true # 启用对LINQ查询的对齐
|
||||
resharper_align_multiline_argument = true # 启用多行参数的对齐
|
||||
resharper_align_multiline_array_and_object_initializer = true # 启用多行数组和对象初始化器的对齐
|
||||
resharper_align_multiline_binary_patterns = true # 启用多行二元模式的对齐
|
||||
resharper_align_multiline_calls_chain = true # 启用多行调用链的对齐
|
||||
resharper_align_multiline_extends_list = true # 启用多行扩展列表的对齐
|
||||
resharper_align_multiline_parameter = true # 启用多行参数的对齐
|
||||
resharper_align_multiline_property_pattern = true # 启用多行属性模式的对齐
|
||||
resharper_align_multiline_switch_expression = true # 启用多行切换表达式的对齐
|
||||
resharper_align_multiple_declaration = true # 启用多个声明的对齐
|
||||
resharper_align_multline_type_parameter_constrains = true # 启用多行类型参数约束的对齐
|
||||
resharper_align_multline_type_parameter_list = true # 启用多行类型参数列表的对齐
|
||||
resharper_align_tuple_components = true # 启用元组组件的对齐
|
||||
resharper_allow_comment_after_lbrace = true # 允许在大括号前添加注释
|
||||
resharper_blank_lines_before_single_line_comment = 1 # 在单行注释前添加空行
|
||||
resharper_csharp_empty_block_style = together_same_line # 设置空块的样式为“在一起,在同一行”
|
||||
resharper_csharp_outdent_commas = true # 是否将逗号后的代码退缩
|
||||
resharper_csharp_place_type_constraints_on_same_line = false # 在类型约束上是否保持在同一行
|
||||
resharper_csharp_stick_comment = false # 是否将注释粘贴在代码行的末尾
|
||||
resharper_csharp_wrap_before_comma = true # 在逗号前是否添加换行
|
||||
resharper_indent_nested_for_stmt = true # 是否对嵌套的for语句进行缩进
|
||||
resharper_indent_nested_foreach_stmt = true # 是否对嵌套的foreach语句进行缩进
|
||||
resharper_indent_nested_while_stmt = true # 是否对嵌套的while语句进行缩进
|
||||
resharper_indent_preprocessor_if = usual_indent # 设置预处理器指令`if`的缩进方式
|
||||
resharper_indent_preprocessor_other = usual_indent # 设置其他预处理器指令的缩进方式
|
||||
resharper_int_align = true # 启用整数对齐
|
||||
resharper_keep_existing_arrangement = false # 在重新排列时是否保留现有的布局
|
||||
resharper_place_linq_into_on_new_line = false # 是否将LINQ表达式放在新行
|
||||
resharper_place_simple_embedded_statement_on_same_line = false # 是否将简单的嵌入式语句放在同一行
|
||||
resharper_place_simple_switch_expression_on_single_line = true # 是否将简单的切换表达式放在单行
|
||||
resharper_wrap_before_eq = true # 在等号前是否添加换行
|
||||
resharper_wrap_chained_method_calls = chop_if_long # 是否拆分链式方法调用
|
||||
resharper_wrap_switch_expression = chop_if_long # 是否拆分切换表达式
|
||||
|
||||
# Microsoft .NET properties
|
||||
csharp_indent_braces = false
|
||||
csharp_new_line_before_open_brace = local_functions, methods, types
|
||||
csharp_indent_braces = false # 设置为false表示花括号不跟随代码行缩进
|
||||
csharp_new_line_before_open_brace = local_functions, methods, types # 这里设置为local_functions, methods, types,表示在局部函数、方法和类型定义的开放花括号前应换行
|
1
.github/workflows/README.md
vendored
Normal file
1
.github/workflows/README.md
vendored
Normal file
@ -0,0 +1 @@
|
||||
github workflows
|
33
.github/workflows/release.yml
vendored
Normal file
33
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
filter: tree:0
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
- working-directory: ./src/backend/NSExt
|
||||
run: dotnet build -c Release -f net8.0
|
||||
- working-directory: ./src/backend/NSExt
|
||||
run: dotnet build -c Release -f net9.0
|
||||
- working-directory: ./src/backend/NSExt
|
||||
run: dotnet pack -c Release --no-build
|
||||
- uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
- id: get_version
|
||||
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v}
|
||||
- run: dotnet nuget push ./dist/backend/NSExt/bin/Release/NSExt.${{ steps.get_version.outputs.VERSION }}.nupkg --skip-duplicate --api-key ${{ secrets.NUGET_TOKEN }} --source https://api.nuget.org/v3/index.json
|
10
.gitignore
vendored
10
.gitignore
vendored
@ -29,7 +29,7 @@ x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
# [Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
@ -399,6 +399,8 @@ FodyWeavers.xsd
|
||||
.idea/
|
||||
|
||||
# User Define
|
||||
build/
|
||||
nuget.config
|
||||
*.[Dd]esigner.cs
|
||||
dist/
|
||||
*.[Dd]esigner.cs
|
||||
*.db
|
||||
cache
|
||||
/assets/res/Ln.resx
|
||||
|
30
.template.config/template.json
Normal file
30
.template.config/template.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"author": "nsnail", //必须
|
||||
"classifications": [
|
||||
"Web/WebAPI"
|
||||
], //必须,这个对应模板的Tags
|
||||
"name": "NSExt", //必须,这个对应模板的Templates
|
||||
"identity": "NSExt", //可选,模板的唯一名称
|
||||
"shortName": "lop", //必须,这个对应模板的Short Name
|
||||
"tags": {
|
||||
"language": "C#",
|
||||
"type": "project"
|
||||
},
|
||||
"sourceName": "NSExt", // 可选,要替换的名字
|
||||
"preferNameDirectory": true, // 可选,添加目录
|
||||
"sources": [
|
||||
{
|
||||
"modifiers": [
|
||||
{
|
||||
"exclude": [
|
||||
"**/.vs/**",
|
||||
"**/.idea/**",
|
||||
"**/.git/**",
|
||||
"**/dist/**",
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
[hook "startcommit"]
|
||||
cmdline = code-format.cmd
|
||||
wait = true
|
||||
show = true
|
@ -1,14 +0,0 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
var slnFile = Directory.GetFiles(@".", "*.sln").First();
|
||||
var content = File.ReadAllText(slnFile);
|
||||
content = Regex.Replace(content,@"ProjectSection\(SolutionItems\) = preProject(?:.|\n)*?EndProjectSection",
|
||||
$"""
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
{string.Join('\n',
|
||||
Directory.GetFiles(@".", "*").Where(x => !x.EndsWith(".sln"))
|
||||
.Select(x=>$"\t\t{Path.GetFileName(x)} = {Path.GetFileName(x)}"))}
|
||||
{'\t'}EndProjectSection
|
||||
""");
|
||||
Console.WriteLine(content);
|
||||
File.WriteAllText(slnFile, content);
|
49
CHANGELOG.md
Normal file
49
CHANGELOG.md
Normal file
@ -0,0 +1,49 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
### [2.3.5](https://github.com/nsnail/NSExt/compare/v2.3.2...v2.3.5) (2025-03-26)
|
||||
|
||||
### [2.3.4](https://github.com/nsnail/NSExt/compare/v2.3.2...v2.3.4) (2025-03-06)
|
||||
|
||||
### [2.3.3](https://github.com/nsnail/NSExt/compare/v2.3.2...v2.3.3) (2025-01-02)
|
||||
|
||||
### [2.3.2](https://github.com/nsnail/NSExt/compare/v2.3.1...v2.3.2) (2024-11-25)
|
||||
|
||||
### [2.3.1](https://github.com/nsnail/NSExt/compare/v2.3.0...v2.3.1) (2024-11-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 a stable release of a package should not have a prerelease dependency ([#27](https://github.com/nsnail/NSExt/issues/27)) ([689d956](https://github.com/nsnail/NSExt/commit/689d9560a858c0ac308ec198b041fdc23720beba))
|
||||
|
||||
## [2.3.0](https://github.com/nsnail/NSExt/compare/v2.2.1...v2.3.0) (2024-11-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ 新增一些扩展方法 ([#25](https://github.com/nsnail/NSExt/issues/25)) ([c7c978f](https://github.com/nsnail/NSExt/commit/c7c978fe5cf931e085e40ad1347bdb3f3450dd0a))
|
||||
|
||||
### [2.2.2](https://github.com/nsnail/NSExt/compare/v2.2.1...v2.2.2) (2024-11-25)
|
||||
|
||||
### [2.2.1](https://github.com/nsnail/NSExt/compare/v2.2.0...v2.2.1) (2024-11-25)
|
||||
|
||||
## [2.2.0](https://github.com/nsnail/NSExt/compare/v2.1.0...v2.2.0) (2024-07-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ 新增string扩展函数 IsJsonString ([41208a5](https://github.com/nsnail/NSExt/commit/41208a5d7f63ad595ec985d3616fbce2c9bdc4d0))
|
||||
* ✨ 新增string扩展函数 IsJsonString ([#19](https://github.com/nsnail/NSExt/issues/19)) ([32b7bdf](https://github.com/nsnail/NSExt/commit/32b7bdf700f694fec7f5dcef43dc05244dcd02d5))
|
||||
* ✨ 异步累加器函数 ([#17](https://github.com/nsnail/NSExt/issues/17)) ([3de9d3b](https://github.com/nsnail/NSExt/commit/3de9d3b8d06143c93c9acad43df22eb09242ac29))
|
||||
|
||||
## [2.1.0](https://github.com/nsnail/NSExt/compare/v2.0.11...v2.1.0) (2024-03-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ 异步累加器函数 ([ddf07fc](https://github.com/nsnail/NSExt/commit/ddf07fce5732e576db1512f870196c20f7b297e2))
|
||||
|
||||
### [2.0.11](https://github.com/nsnail/NSExt/compare/v2.0.10...v2.0.11) (2023-12-15)
|
||||
|
||||
### [2.0.10](https://github.com/nsnail/NSExt/compare/v2.0.9...v2.0.10) (2023-12-14)
|
@ -1,16 +0,0 @@
|
||||
var path = Directory.GetFiles(@".idea", "workspace.xml", SearchOption.AllDirectories).First();
|
||||
const string findStr = """
|
||||
"keyToString": {
|
||||
""";
|
||||
const string replaceStr = """
|
||||
"keyToString": {
|
||||
"rider.code.cleanup.on.save": "true",
|
||||
""";
|
||||
var content = File.ReadAllText(path);
|
||||
if(content.Contains("rider.code.cleanup.on.save")){
|
||||
Console.WriteLine("alreay added");
|
||||
return;
|
||||
}
|
||||
content = content.Replace(findStr, replaceStr);
|
||||
Console.WriteLine(content);
|
||||
File.WriteAllText(path, content);
|
@ -1,17 +0,0 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<CodeAnalysisRuleSet>../StyleCopAnalyzers.ruleset</CodeAnalysisRuleSet>
|
||||
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
<MSBuildTreatWarningsAsErrors>true</MSBuildTreatWarningsAsErrors>
|
||||
<MSBuildWarningsAsErrors>true</MSBuildWarningsAsErrors>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors>true</WarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,22 +1,37 @@
|
||||
<!-- 注意此文件名大小写不可变更 -->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<BaseOutputPath>../dist</BaseOutputPath>
|
||||
<BaseIntermediateOutputPath>../dist</BaseIntermediateOutputPath>
|
||||
<OutputPath>$(BaseOutputPath)/$(MSBuildProjectName)/bin</OutputPath>
|
||||
<IntermediateOutputPath>$(BaseIntermediateOutputPath)/$(MSBuildProjectName)/obj</IntermediateOutputPath>
|
||||
<MSBuildProjectExtensionsPath>$(BaseIntermediateOutputPath)/$(MSBuildProjectName)/obj</MSBuildProjectExtensionsPath>
|
||||
<Authors>nsnail</Authors>
|
||||
<Product>NSExt</Product>
|
||||
<Copyright>© 2006-2022 nsnail</Copyright>
|
||||
<RepositoryUrl>https://github.com/nsnail/ns-ext.git</RepositoryUrl>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<EmbedUntrackedSource>true</EmbedUntrackedSource>
|
||||
<EmbedAllSources>true</EmbedAllSources>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageProjectUrl>https://github.com/nsnail/ns-ext.git</PackageProjectUrl>
|
||||
<SolutionDir>$(MSBuildThisFileDirectory)</SolutionDir>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)/build/minver.targets"/>
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Authors>nsnail</Authors>
|
||||
<BaseIntermediateOutputPath>$(SolutionDir)/dist/backend/$(MSBuildProjectName)/obj</BaseIntermediateOutputPath>
|
||||
<BaseOutputPath>$(SolutionDir)/dist/backend/$(MSBuildProjectName)/bin</BaseOutputPath>
|
||||
<Copyright>© 2006-2023 nsnail</Copyright>
|
||||
<Description>The NSExt is a .NET extension function library</Description>
|
||||
<EnableBaseIntermediateOutputPathMismatchWarning>false</EnableBaseIntermediateOutputPathMismatchWarning>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<MinVerDefaultPreReleaseIdentifiers>beta</MinVerDefaultPreReleaseIdentifiers>
|
||||
<MinVerTagPrefix>v</MinVerTagPrefix>
|
||||
<NoWarn>CA1707;CA1720;CA5350;CA5351;IDE0005;IDE0008;IDE0010;IDE0028;IDE0048;IDE0055;IDE0160;IDE0300;IDE0305;RCS1123;RCS1141;RCS1142;RCS1181;S101;S1075;S1121;S1135;S125;S2094;S3604;S3928;S4663;SA1010;SA1407;SYSLIB1045</NoWarn>
|
||||
<Product>NSExt</Product>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/nsnail/NSExt.git</RepositoryUrl>
|
||||
<RootNamespace>NSExt</RootNamespace>
|
||||
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
|
||||
<Title>$(AssemblyName)</Title>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MinVer" Version="6.1.0-beta.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="../GlobalUsings.cs" Link="GlobalUsings.cs"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,45 +0,0 @@
|
||||
/*
|
||||
for %%i in (*.png) do pngquant %%i --force --output %%i --skip-if-larger
|
||||
for %%i in (*.jpg) do jpegtran -copy none -optimize -perfect %%i %%i
|
||||
*
|
||||
*/
|
||||
|
||||
var files = Directory.EnumerateFiles(".", "*.png"
|
||||
, new EnumerationOptions {
|
||||
RecurseSubdirectories = true
|
||||
, AttributesToSkip = FileAttributes.ReparsePoint
|
||||
, IgnoreInaccessible = true
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
|
||||
Parallel.ForEach(files, file => {
|
||||
var startInfo = new ProcessStartInfo {
|
||||
FileName = "pngquant"
|
||||
, Arguments
|
||||
= $"\"{file}\" --force --output \"{file}\" --skip-if-larger"
|
||||
};
|
||||
using var p = Process.Start(startInfo);
|
||||
p.WaitForExit();
|
||||
Console.WriteLine($"{file}: {p.ExitCode}");
|
||||
});
|
||||
|
||||
files = new[] { "*.jpg", "*.jpeg" }
|
||||
.SelectMany(x => Directory.EnumerateFiles(
|
||||
".", x
|
||||
, new EnumerationOptions {
|
||||
RecurseSubdirectories = true
|
||||
, AttributesToSkip = FileAttributes.ReparsePoint
|
||||
, IgnoreInaccessible = true
|
||||
}))
|
||||
.ToArray();
|
||||
|
||||
Parallel.ForEach(files, file => {
|
||||
var startInfo = new ProcessStartInfo {
|
||||
FileName = "jpegtran"
|
||||
, Arguments = $"-copy none -optimize -perfect \"{file}\" \"{file}\""
|
||||
};
|
||||
using var p = Process.Start(startInfo);
|
||||
p.WaitForExit();
|
||||
Console.WriteLine($"{file}: {p.ExitCode}");
|
||||
});
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 nsnail
|
||||
Copyright (c) 2023 nsnail
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
124
NSExt.sln
124
NSExt.sln
@ -3,44 +3,94 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NSExt", "src\NSExt.csproj", "{70DD1C27-7ACB-4BE0-A9CD-D781E4050DE5}"
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C84EB5A0-37AD-4B17-A51E-E36888C4441E}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "meta", "meta", "{85E669CB-FC0A-4C1D-92DE-38D0662C257D}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
.gitattributes = .gitattributes
|
||||
.gitignore = .gitignore
|
||||
.tgitconfig = .tgitconfig
|
||||
AddMetaFilesToSln.csx = AddMetaFilesToSln.csx
|
||||
build.cake = build.cake
|
||||
code-format.cmd = code-format.cmd
|
||||
CodeCleanupOnSave.csx = CodeCleanupOnSave.csx
|
||||
CodeQuality.props = CodeQuality.props
|
||||
Directory.Build.props = Directory.Build.props
|
||||
dot.sln.DotSettings = dot.sln.DotSettings
|
||||
dotnet-tools.json = dotnet-tools.json
|
||||
git-clean.cmd = git-clean.cmd
|
||||
global.json = global.json
|
||||
ImageOptimize.csx = ImageOptimize.csx
|
||||
LICENSE = LICENSE
|
||||
push2nuget.ps1 = push2nuget.ps1
|
||||
README.md = README.md
|
||||
stylecop.json = stylecop.json
|
||||
StyleCopAnalyzers.ruleset = StyleCopAnalyzers.ruleset
|
||||
EndProjectSection
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "backend", "backend", "{4DAF9366-855F-46BB-AE4C-660C92FA0697}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "meta", "meta", "{5198A03D-0CAC-4828-A807-34A693F73859}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.commitlintrc.js = .commitlintrc.js
|
||||
.editorconfig = .editorconfig
|
||||
.gitattributes = .gitattributes
|
||||
.gitignore = .gitignore
|
||||
CHANGELOG.md = CHANGELOG.md
|
||||
Directory.Build.props = Directory.Build.props
|
||||
dotnet-tools.json = dotnet-tools.json
|
||||
global.json = global.json
|
||||
key.snk = key.snk
|
||||
LICENSE = LICENSE
|
||||
NSExt.sln.DotSettings = NSExt.sln.DotSettings
|
||||
nuget.config = nuget.config
|
||||
package.json = package.json
|
||||
README.md = README.md
|
||||
README.zh-CN.md = README.zh-CN.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{BB0B25C9-0901-4923-913F-00F9A6B352A5}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
1.git.pull.request.ps1 = scripts/1.git.pull.request.ps1
|
||||
2.git.release.ps1 = scripts/2.git.release.ps1
|
||||
3.git.recreate.branch.ps1 = scripts/3.git.recreate.branch.ps1
|
||||
4.git.del.obsolete.tags.ps1 = scripts/4.git.del.obsolete.tags.ps1
|
||||
clean.ln.csx = scripts/clean.ln.csx
|
||||
code.clean.csx = scripts/code.clean.csx
|
||||
code.clean.ps1 = scripts/code.clean.ps1
|
||||
find.unused.ln.csx = scripts/find.unused.ln.csx
|
||||
gen.cs.tt = scripts/gen.cs.tt
|
||||
gen.ln.cmd = scripts/gen.ln.cmd
|
||||
gen.resx.tt = scripts/gen.resx.tt
|
||||
image.optimize.csx = scripts/image.optimize.csx
|
||||
install.as.tpl.ps1 = scripts/install.as.tpl.ps1
|
||||
rename.csx = scripts/rename.csx
|
||||
resharper.full.ps1 = scripts/resharper.full.ps1
|
||||
switch.nuget.or.project.csx = scripts/switch.nuget.or.project.csx
|
||||
sync.sln.files.csx = scripts/sync.sln.files.csx
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{1129FE25-466B-4F4F-85FC-3752664245E1}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{3C6F049E-3EE8-4D66-9AFF-E8A369032487}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
README.md = .github/workflows/README.md
|
||||
release.yml = .github/workflows/release.yml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{8E4C93BA-9493-4892-80C4-5E174C504829}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
code.quality.props = build/code.quality.props
|
||||
copy.pkg.xml.comment.files.targets = build/copy.pkg.xml.comment.files.targets
|
||||
minver.targets = build/minver.targets
|
||||
nuget.package.props = build/nuget.package.props
|
||||
prebuild.targets = build/prebuild.targets
|
||||
stylecop.analyzers.ruleset = build/stylecop.analyzers.ruleset
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NSExt", "src\backend\NSExt\NSExt.csproj", "{1E62C322-EE42-4699-A6F1-791C53EFA62D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NSExt.Tests", "src\backend\NSExt.Tests\NSExt.Tests.csproj", "{00604162-C444-478B-B773-3AB23C856CA7}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{70DD1C27-7ACB-4BE0-A9CD-D781E4050DE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{70DD1C27-7ACB-4BE0-A9CD-D781E4050DE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{70DD1C27-7ACB-4BE0-A9CD-D781E4050DE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{70DD1C27-7ACB-4BE0-A9CD-D781E4050DE5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{1E62C322-EE42-4699-A6F1-791C53EFA62D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1E62C322-EE42-4699-A6F1-791C53EFA62D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1E62C322-EE42-4699-A6F1-791C53EFA62D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1E62C322-EE42-4699-A6F1-791C53EFA62D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{00604162-C444-478B-B773-3AB23C856CA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{00604162-C444-478B-B773-3AB23C856CA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{00604162-C444-478B-B773-3AB23C856CA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{00604162-C444-478B-B773-3AB23C856CA7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{4DAF9366-855F-46BB-AE4C-660C92FA0697} = {C84EB5A0-37AD-4B17-A51E-E36888C4441E}
|
||||
{3C6F049E-3EE8-4D66-9AFF-E8A369032487} = {1129FE25-466B-4F4F-85FC-3752664245E1}
|
||||
{1E62C322-EE42-4699-A6F1-791C53EFA62D} = {4DAF9366-855F-46BB-AE4C-660C92FA0697}
|
||||
{00604162-C444-478B-B773-3AB23C856CA7} = {4DAF9366-855F-46BB-AE4C-660C92FA0697}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
@ -1,17 +1,37 @@
|
||||
<wpf:ResourceDictionary xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xml:space="preserve">
|
||||
<s:Boolean x:Key="/Default/ReSpeller/ReSpellerEnabled/@EntryValue">False</s:Boolean>
|
||||
<!-- CodeEditing-->
|
||||
<s:Boolean x:Key="/Default/CodeEditing/TypingAssist/SkipClosingBracesOnTabInStringLiterals/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeEditing/GenerateMemberBody/DocumentationGenerationKind/@EntryValue">Inherit</s:String>
|
||||
|
||||
<!-- CodeInspection-->
|
||||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ett/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/AnalysisEnabled/@EntryValue">OFF</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=AssignmentInConditionalExpression/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ClassNeverInstantiated_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=DuplicateResource/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantPatternParentheses/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestBaseTypeForParameter/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestBaseTypeForParameterInConstructor/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=TooWideLocalVariableScope/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedAutoPropertyAccessor_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedAutoPropertyAccessor_002ELocal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
|
||||
<!-- CodeStyle-->
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/AnalysisEnabled/@EntryValue">OFF</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=TooWideLocalVariableScope/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_WHILE/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Constants/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateConstants/@EntryIndexedValue"><Policy Inspect="True" Prefix="_" Suffix="" Style="AA_BB" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue"><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=StaticReadonly/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_RECORD_FIELD_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue"><Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></Policy></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=236f7aa5_002D7b06_002D43ca_002Dbf2a_002D9b31bfcff09a/@EntryIndexedValue"><Policy><Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"><ElementKinds><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="AA_BB" /></Policy></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=669e5282_002Dfb4b_002D4e90_002D91e7_002D07d269d04b60/@EntryIndexedValue"><Policy><Descriptor Staticness="Any" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Constant fields (not private)"><ElementKinds><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></Policy></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a0b4bc4d_002Dd13b_002D4a37_002Db37e_002Dc9c6864e4302/@EntryIndexedValue"><Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=c873eafb_002Dd57f_002D481d_002D8c93_002D77f6863c2f88/@EntryIndexedValue"><Policy><Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static readonly fields (not private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy></s:String>
|
||||
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpFileLayoutPatterns/Pattern/@EntryValue"><?xml version="1.0" encoding="utf-16"?>
|
||||
<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns">
|
||||
@ -54,4 +74,21 @@
|
||||
</TypePattern>
|
||||
</Patterns></s:String>
|
||||
|
||||
<!-- Environment-->
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002EMemberReordering_002EMigrations_002ECSharpFileLayoutPatternRemoveIsAttributeUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
<!-- ReSpeller-->
|
||||
<s:Boolean x:Key="/Default/ReSpeller/ReSpellerEnabled/@EntryValue">True</s:Boolean>
|
||||
|
||||
<!-- UserDictionary-->
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=FLG/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=FLGL/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=RGX/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=RGXL/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
</wpf:ResourceDictionary>
|
1
assets/README.md
Normal file
1
assets/README.md
Normal file
@ -0,0 +1 @@
|
||||
# 资源文件目录
|
BIN
assets/logo.png
Normal file
BIN
assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
132
build.cake
132
build.cake
@ -1,132 +0,0 @@
|
||||
var target = Argument("target", "Default");
|
||||
var configuration = Argument("configuration", "Release");
|
||||
var pkgOutPath = $"./dist/NSExt/pkg/{configuration}";
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Tasks
|
||||
|
||||
Task("Clean")
|
||||
.Does(context =>
|
||||
{
|
||||
context.CleanDirectory(pkgOutPath);
|
||||
});
|
||||
|
||||
Task("Build")
|
||||
.IsDependentOn("Clean")
|
||||
.Does(context =>
|
||||
{
|
||||
DotNetBuild("./NSExt.sln", new DotNetBuildSettings {
|
||||
Configuration = configuration,
|
||||
NoIncremental = context.HasArgument("rebuild"),
|
||||
MSBuildSettings = new DotNetMSBuildSettings()
|
||||
.TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error)
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Task("Test")
|
||||
.IsDependentOn("Build")
|
||||
.Does(context =>
|
||||
{
|
||||
DotNetTest("./test/Spectre.Console.Tests/Spectre.Console.Tests.csproj", new DotNetTestSettings {
|
||||
Configuration = configuration,
|
||||
NoRestore = true,
|
||||
NoBuild = true,
|
||||
});
|
||||
|
||||
DotNetTest("./test/Spectre.Console.Cli.Tests/Spectre.Console.Cli.Tests.csproj", new DotNetTestSettings {
|
||||
Configuration = configuration,
|
||||
NoRestore = true,
|
||||
NoBuild = true,
|
||||
});
|
||||
|
||||
DotNetTest("./test/Spectre.Console.Analyzer.Tests/Spectre.Console.Analyzer.Tests.csproj", new DotNetTestSettings {
|
||||
Configuration = configuration,
|
||||
NoRestore = true,
|
||||
NoBuild = true,
|
||||
});
|
||||
});
|
||||
|
||||
Task("Package")
|
||||
.IsDependentOn("Build")
|
||||
.Does(context =>
|
||||
{
|
||||
context.DotNetPack("./NSExt.sln", new DotNetPackSettings {
|
||||
Configuration = configuration,
|
||||
NoRestore = true,
|
||||
NoBuild = true,
|
||||
OutputDirectory = pkgOutPath,
|
||||
MSBuildSettings = new DotNetMSBuildSettings()
|
||||
.TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error)
|
||||
});
|
||||
});
|
||||
|
||||
Task("Publish-GitHub")
|
||||
.WithCriteria(ctx => BuildSystem.IsRunningOnGitHubActions, "Not running on GitHub Actions")
|
||||
.IsDependentOn("Package")
|
||||
.Does(context =>
|
||||
{
|
||||
var apiKey = Argument<string>("github-key", null);
|
||||
if(string.IsNullOrWhiteSpace(apiKey)) {
|
||||
throw new CakeException("No GitHub API key was provided.");
|
||||
}
|
||||
|
||||
// Publish to GitHub Packages
|
||||
var exitCode = 0;
|
||||
foreach(var file in context.GetFiles("./.artifacts/*.nupkg"))
|
||||
{
|
||||
context.Information("Publishing {0}...", file.GetFilename().FullPath);
|
||||
exitCode += StartProcess("dotnet",
|
||||
new ProcessSettings {
|
||||
Arguments = new ProcessArgumentBuilder()
|
||||
.Append("gpr")
|
||||
.Append("push")
|
||||
.AppendQuoted(file.FullPath)
|
||||
.AppendSwitchSecret("-k", " ", apiKey)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if(exitCode != 0)
|
||||
{
|
||||
throw new CakeException("Could not push GitHub packages.");
|
||||
}
|
||||
});
|
||||
|
||||
Task("Publish-NuGet")
|
||||
// .WithCriteria(ctx => BuildSystem.IsRunningOnGitHubActions, "Not running on GitHub Actions")
|
||||
.IsDependentOn("Package")
|
||||
.Does(context =>
|
||||
{
|
||||
var apiKey = Argument<string>("nuget-key", null);
|
||||
if(string.IsNullOrWhiteSpace(apiKey)) {
|
||||
throw new CakeException("No NuGet API key was provided.");
|
||||
}
|
||||
|
||||
// Publish to GitHub Packages
|
||||
foreach(var file in context.GetFiles($"{pkgOutPath}/*.*nupkg"))
|
||||
{
|
||||
context.Information("Publishing {0}...", file.GetFilename().FullPath);
|
||||
DotNetNuGetPush(file.FullPath, new DotNetNuGetPushSettings
|
||||
{
|
||||
Source = "https://api.nuget.org/v3/index.json",
|
||||
ApiKey = apiKey,
|
||||
SkipDuplicate = true
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Targets
|
||||
|
||||
Task("Publish")
|
||||
//.IsDependentOn("Publish-GitHub")
|
||||
.IsDependentOn("Publish-NuGet");
|
||||
|
||||
Task("Default")
|
||||
.IsDependentOn("Package");
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Execution
|
||||
|
||||
RunTarget(target)
|
47
build/code.quality.props
Normal file
47
build/code.quality.props
Normal file
@ -0,0 +1,47 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<CodeAnalysisRuleSet>$(SolutionDir)/build/stylecop.analyzers.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
<MSBuildTreatWarningsAsErrors>true</MSBuildTreatWarningsAsErrors>
|
||||
<MSBuildWarningsAsErrors>true</MSBuildWarningsAsErrors>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors>true</WarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.13.61">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.Analyzers" Version="4.13.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.7.0.110445">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="AsyncSuffixAnalyzer" Version="1.0.6285.32977">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="AsyncFixer" Version="1.6.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="ProductiveRage.SealedClassVerification.Net" Version="1.7.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<!-- <PackageReference Include="NSCodeAnalysis" Version="1.0.1-alpha.0.2">-->
|
||||
<!-- <PrivateAssets>all</PrivateAssets>-->
|
||||
<!-- <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>-->
|
||||
<!-- </PackageReference>-->
|
||||
</ItemGroup>
|
||||
</Project>
|
10
build/copy.pkg.xml.comment.files.targets
Normal file
10
build/copy.pkg.xml.comment.files.targets
Normal file
@ -0,0 +1,10 @@
|
||||
<Project>
|
||||
<Target Name="AfterTargetsBuild" AfterTargets="Build">
|
||||
<ItemGroup>
|
||||
<PackageReferenceFiles
|
||||
Condition="%(PackageReference.CopyToOutputDirectory) != ''"
|
||||
Include="$(NugetPackageRoot)\%(PackageReference.Identity)\%(PackageReference.Version)\%(PackageReference.CopyToOutputDirectory)" />
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(PackageReferenceFiles)" DestinationFolder="$(OutDir)" />
|
||||
</Target>
|
||||
</Project>
|
11
build/minver.targets
Normal file
11
build/minver.targets
Normal file
@ -0,0 +1,11 @@
|
||||
<Project>
|
||||
<Target Name="MyTarget" AfterTargets="MinVer">
|
||||
<PropertyGroup>
|
||||
<AssemblyVersion>$(MinVerMajor).$(MinVerMinor).$(MinVerPatch)</AssemblyVersion>
|
||||
<FileVersion>$(MinVerMajor).$(MinVerMinor).$(MinVerPatch)</FileVersion>
|
||||
<PackageVersion>$(MinVerVersion)</PackageVersion>
|
||||
<ProductVersion>$(MinVerVersion)</ProductVersion>
|
||||
<Version>$(MinVerVersion)</Version>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
</Project>
|
20
build/nuget.package.props
Normal file
20
build/nuget.package.props
Normal file
@ -0,0 +1,20 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<AssemblyOriginatorKeyFile>../../../key.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<EmbedAllSources>true</EmbedAllSources>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<IsPackable>true</IsPackable>
|
||||
<PackageIcon>logo.png</PackageIcon>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageTags>extensions</PackageTags>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Include="../../../assets/logo.png" Pack="true" PackagePath="/"/>
|
||||
<None Include="../../../README.md" Pack="true" PackagePath="/"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
24
build/prebuild.targets
Normal file
24
build/prebuild.targets
Normal file
@ -0,0 +1,24 @@
|
||||
<Project>
|
||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||
<Exec Command="dotnet tool restore" StdOutEncoding="utf-8"/>
|
||||
<Exec Condition="!Exists('$(SolutionDir)/assets/res/Ln.resx')" WorkingDirectory="$(SolutionDir)/scripts"
|
||||
Command="dotnet t4 ./gen.resx.tt -o ../assets/res/Ln.resx"
|
||||
StdOutEncoding="utf-8"/>
|
||||
<Exec Condition="!Exists('$(SolutionDir)/dist/backend/$(ProjectName)/Ln.cs')"
|
||||
WorkingDirectory="$(SolutionDir)/scripts"
|
||||
Command="dotnet t4 ./gen.cs.tt -o ../dist/backend/$(ProjectName)/Ln.cs"
|
||||
StdOutEncoding="utf-8"/>
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<None Include="$(SolutionDir)/assets/res/Statements.ln">
|
||||
<Link>Languages/Statements.ln</Link>
|
||||
</None>
|
||||
<EmbeddedResource Include="$(SolutionDir)/assets/res/Ln.resx">
|
||||
<Link>Languages/Ln.resx</Link>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="$(SolutionDir)/dist/backend/$(ProjectName)/Ln.Designer.cs">
|
||||
<Link>Languages/Ln.Designer.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -6,14 +6,13 @@
|
||||
<Rule Id="SA0001" Action="Warning"/> <!-- XML comment analysis disabled -->
|
||||
<Rule Id="SA0002" Action="Warning"/> <!-- Invalid settings file -->
|
||||
</Rules>
|
||||
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.SpacingRules">
|
||||
<Rule Id="SA1000" Action="Warning"/> <!-- Keywords should be spaced correctly -->
|
||||
<Rule Id="SA1001" Action="None"/> <!-- Commas should be spaced correctly -->
|
||||
<Rule Id="SA1002" Action="Warning"/> <!-- Semicolons should be spaced correctly -->
|
||||
<Rule Id="SA1003" Action="Warning"/> <!-- Symbols should be spaced correctly -->
|
||||
<Rule Id="SA1004" Action="Warning"/> <!-- Documentation lines should begin with single space -->
|
||||
<Rule Id="SA1005" Action="None"/> <!-- Single line comments should begin with single space -->
|
||||
<Rule Id="SA1005" Action="Warning"/> <!-- Single line comments should begin with single space -->
|
||||
<Rule Id="SA1006" Action="Warning"/> <!-- Preprocessor keywords should not be preceded by space -->
|
||||
<Rule Id="SA1007" Action="Warning"/> <!-- Operator keyword should be followed by space -->
|
||||
<Rule Id="SA1008" Action="None"/> <!-- Opening parenthesis should be spaced correctly -->
|
||||
@ -31,34 +30,41 @@
|
||||
<Rule Id="SA1020" Action="Warning"/> <!-- Increment decrement symbols should be spaced correctly -->
|
||||
<Rule Id="SA1021" Action="Warning"/> <!-- Negative signs should be spaced correctly -->
|
||||
<Rule Id="SA1022" Action="Warning"/> <!-- Positive signs should be spaced correctly -->
|
||||
<Rule Id="SA1023" Action="Warning"/> <!-- Dereference and access of symbols should be spaced correctly -->
|
||||
<Rule Id="SA1023"
|
||||
Action="Warning"/> <!-- Dereference and access of symbols should be spaced correctly -->
|
||||
<Rule Id="SA1024" Action="Warning"/> <!-- Colons should be spaced correctly -->
|
||||
<Rule Id="SA1025" Action="None"/> <!-- Code should not contain multiple whitespace in a row -->
|
||||
<Rule Id="SA1026" Action="Warning"/> <!-- Code should not contain space after new or stackalloc keyword in implicitly typed array allocation -->
|
||||
<Rule Id="SA1026"
|
||||
Action="Warning"/> <!-- Code should not contain space after new or stackalloc keyword in implicitly typed array allocation -->
|
||||
<Rule Id="SA1027" Action="Warning"/> <!-- Use tabs correctly -->
|
||||
<Rule Id="SA1028" Action="Warning"/> <!-- Code should not contain trailing whitespace -->
|
||||
</Rules>
|
||||
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.ReadabilityRules">
|
||||
<Rule Id="SA1100" Action="Warning"/> <!-- Do not prefix calls with base unless local implementation exists -->
|
||||
<Rule Id="SA1100"
|
||||
Action="Warning"/> <!-- Do not prefix calls with base unless local implementation exists -->
|
||||
<Rule Id="SA1101" Action="None"/> <!-- Prefix local calls with this -->
|
||||
<Rule Id="SA1102" Action="Warning"/> <!-- Query clause should follow previous clause -->
|
||||
<Rule Id="SA1103" Action="Warning"/> <!-- Query clauses should be on separate lines or all on one line -->
|
||||
<Rule Id="SA1104" Action="Warning"/> <!-- Query clause should begin on new line when previous clause spans multiple lines -->
|
||||
<Rule Id="SA1105" Action="Warning"/> <!-- Query clauses spanning multiple lines should begin on own line -->
|
||||
<Rule Id="SA1103"
|
||||
Action="Warning"/> <!-- Query clauses should be on separate lines or all on one line -->
|
||||
<Rule Id="SA1104"
|
||||
Action="Warning"/> <!-- Query clause should begin on new line when previous clause spans multiple lines -->
|
||||
<Rule Id="SA1105"
|
||||
Action="Warning"/> <!-- Query clauses spanning multiple lines should begin on own line -->
|
||||
<Rule Id="SA1106" Action="Warning"/> <!-- Code should not contain empty statements -->
|
||||
<Rule Id="SA1107" Action="Warning"/> <!-- Code should not contain multiple statements on one line -->
|
||||
<Rule Id="SA1108" Action="Warning"/> <!-- Block statements should not contain embedded comments -->
|
||||
<Rule Id="SA1109" Action="Warning"/> <!-- Block statements should not contain embedded regions -->
|
||||
<Rule Id="SA1110" Action="Warning"/> <!-- Opening parenthesis or bracket should be on declaration line -->
|
||||
<Rule Id="SA1110"
|
||||
Action="Warning"/> <!-- Opening parenthesis or bracket should be on declaration line -->
|
||||
<Rule Id="SA1111" Action="Warning"/> <!-- Closing parenthesis should be on line of last parameter -->
|
||||
<Rule Id="SA1112" Action="Warning"/> <!-- Closing parenthesis should be on line of opening parenthesis -->
|
||||
<Rule Id="SA1112"
|
||||
Action="Warning"/> <!-- Closing parenthesis should be on line of opening parenthesis -->
|
||||
<Rule Id="SA1113" Action="None"/> <!-- Comma should be on the same line as previous parameter -->
|
||||
<Rule Id="SA1114" Action="Warning"/> <!-- Parameter list should follow declaration -->
|
||||
<Rule Id="SA1115" Action="Warning"/> <!-- Parameter should follow comma -->
|
||||
<Rule Id="SA1116" Action="Warning"/> <!-- Split parameters should start on line after declaration -->
|
||||
<Rule Id="SA1117" Action="None"/> <!-- Parameters should be on same line or separate lines -->
|
||||
<Rule Id="SA1118" Action="Warning"/> <!-- Parameter should not span multiple lines -->
|
||||
<Rule Id="SA1118" Action="None"/> <!-- Parameter should not span multiple lines -->
|
||||
<Rule Id="SA1120" Action="None"/> <!-- Comments should contain text -->
|
||||
<Rule Id="SA1121" Action="Warning"/> <!-- Use built-in type alias -->
|
||||
<Rule Id="SA1122" Action="Warning"/> <!-- Use string.Empty for empty strings -->
|
||||
@ -73,14 +79,13 @@
|
||||
<Rule Id="SA1131" Action="Warning"/> <!-- Use readable conditions -->
|
||||
<Rule Id="SA1132" Action="Warning"/> <!-- Do not combine fields -->
|
||||
<Rule Id="SA1133" Action="Warning"/> <!-- Do not combine attributes -->
|
||||
<Rule Id="SA1134" Action="None"/> <!-- Attributes should not share line -->
|
||||
<Rule Id="SA1134" Action="Warning"/> <!-- Attributes should not share line -->
|
||||
<Rule Id="SA1135" Action="Warning"/> <!-- Using directives should be qualified -->
|
||||
<Rule Id="SA1136" Action="Warning"/> <!-- Enum values should be on separate lines -->
|
||||
<Rule Id="SA1137" Action="Warning"/> <!-- Elements should have the same indentation -->
|
||||
<Rule Id="SA1139" Action="Warning"/> <!-- Use literal suffix notation instead of casting -->
|
||||
<Rule Id="SX1101" Action="Warning"/> <!-- Do not prefix local calls with 'this.' -->
|
||||
</Rules>
|
||||
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.OrderingRules">
|
||||
<Rule Id="SA1200" Action="None"/> <!-- Using directives should be placed correctly -->
|
||||
<Rule Id="SA1201" Action="Warning"/> <!-- Elements should appear in the correct order -->
|
||||
@ -90,79 +95,87 @@
|
||||
<Rule Id="SA1205" Action="Warning"/> <!-- Partial elements should declare access -->
|
||||
<Rule Id="SA1206" Action="Warning"/> <!-- Declaration keywords should follow order -->
|
||||
<Rule Id="SA1207" Action="Warning"/> <!-- Protected should come before internal -->
|
||||
<Rule Id="SA1208" Action="Warning"/> <!-- System using directives should be placed before other using directives -->
|
||||
<Rule Id="SA1209" Action="Warning"/> <!-- Using alias directives should be placed after other using directives -->
|
||||
<Rule Id="SA1210" Action="Warning"/> <!-- Using directives should be ordered alphabetically by namespace -->
|
||||
<Rule Id="SA1211" Action="Warning"/> <!-- Using alias directives should be ordered alphabetically by alias name -->
|
||||
<Rule Id="SA1208"
|
||||
Action="Warning"/> <!-- System using directives should be placed before other using directives -->
|
||||
<Rule Id="SA1209"
|
||||
Action="Warning"/> <!-- Using alias directives should be placed after other using directives -->
|
||||
<Rule Id="SA1210"
|
||||
Action="Warning"/> <!-- Using directives should be ordered alphabetically by namespace -->
|
||||
<Rule Id="SA1211"
|
||||
Action="Warning"/> <!-- Using alias directives should be ordered alphabetically by alias name -->
|
||||
<Rule Id="SA1212" Action="Warning"/> <!-- Property accessors should follow order -->
|
||||
<Rule Id="SA1213" Action="Warning"/> <!-- Event accessors should follow order -->
|
||||
<Rule Id="SA1214" Action="Warning"/> <!-- Readonly fields should appear before non-readonly fields -->
|
||||
<Rule Id="SA1216" Action="Warning"/> <!-- Using static directives should be placed at the correct location -->
|
||||
<Rule Id="SA1216"
|
||||
Action="Warning"/> <!-- Using static directives should be placed at the correct location -->
|
||||
<Rule Id="SA1217" Action="Warning"/> <!-- Using static directives should be ordered alphabetically -->
|
||||
</Rules>
|
||||
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.NamingRules">
|
||||
<Rule Id="SA1300" Action="Warning"/> <!-- Element should begin with upper-case letter -->
|
||||
<Rule Id="SA1301" Action="Warning"/> <!-- Element should begin with lower-case letter -->
|
||||
<Rule Id="SA1302" Action="Warning"/> <!-- Interface names should begin with I -->
|
||||
<Rule Id="SA1303" Action="Warning"/> <!-- Const field names should begin with upper-case letter -->
|
||||
<Rule Id="SA1304" Action="Warning"/> <!-- Non-private readonly fields should begin with upper-case letter -->
|
||||
<Rule Id="SA1304"
|
||||
Action="Warning"/> <!-- Non-private readonly fields should begin with upper-case letter -->
|
||||
<Rule Id="SA1305" Action="None"/> <!-- Field names should not use Hungarian notation -->
|
||||
<Rule Id="SA1306" Action="Warning"/> <!-- Field names should begin with lower-case letter -->
|
||||
<Rule Id="SA1307" Action="Warning"/> <!-- Accessible fields should begin with upper-case letter -->
|
||||
<Rule Id="SA1308" Action="Warning"/> <!-- Variable names should not be prefixed -->
|
||||
<Rule Id="SA1309" Action="None"/> <!-- Field names should not begin with underscore -->
|
||||
<Rule Id="SA1310" Action="None"/> <!-- Field names should not contain underscore -->
|
||||
<Rule Id="SA1311" Action="Warning"/> <!-- Static readonly fields should begin with upper-case letter -->
|
||||
<Rule Id="SA1311"
|
||||
Action="Warning"/> <!-- Static readonly fields should begin with upper-case letter -->
|
||||
<Rule Id="SA1312" Action="Warning"/> <!-- Variable names should begin with lower-case letter -->
|
||||
<Rule Id="SA1313" Action="None"/> <!-- Parameter names should begin with lower-case letter -->
|
||||
<Rule Id="SA1314" Action="Warning"/> <!-- Type parameter names should begin with T -->
|
||||
<Rule Id="SX1309" Action="Warning"/> <!-- Field names should begin with underscore -->
|
||||
<Rule Id="SX1309S" Action="Warning"/> <!-- Static field names should begin with underscore -->
|
||||
</Rules>
|
||||
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.MaintainabilityRules">
|
||||
<Rule Id="SA1119" Action="Warning"/> <!-- Statement should not use unnecessary parenthesis -->
|
||||
<Rule Id="SA1400" Action="Warning"/> <!-- Access modifier should be declared -->
|
||||
<Rule Id="SA1401" Action="Warning"/> <!-- Fields should be private -->
|
||||
<Rule Id="SA1402" Action="Warning"/> <!-- File may only contain a single type -->
|
||||
<Rule Id="SA1402" Action="None"/> <!-- File may only contain a single type -->
|
||||
<Rule Id="SA1403" Action="Warning"/> <!-- File may only contain a single namespace -->
|
||||
<Rule Id="SA1404" Action="Warning"/> <!-- Code analysis suppression should have justification -->
|
||||
<Rule Id="SA1405" Action="Warning"/> <!-- Debug.Assert should provide message text -->
|
||||
<Rule Id="SA1406" Action="Warning"/> <!-- Debug.Fail should provide message text -->
|
||||
<Rule Id="SA1407" Action="None"/> <!-- Arithmetic expressions should declare precedence -->
|
||||
<Rule Id="SA1407" Action="Warning"/> <!-- Arithmetic expressions should declare precedence -->
|
||||
<Rule Id="SA1408" Action="Warning"/> <!-- Conditional expressions should declare precedence -->
|
||||
<Rule Id="SA1409" Action="Warning"/> <!-- Remove unnecessary code -->
|
||||
<Rule Id="SA1410" Action="Warning"/> <!-- Remove delegate parenthesis when possible -->
|
||||
<Rule Id="SA1411" Action="Warning"/> <!-- Attribute constructor should not use unnecessary parenthesis -->
|
||||
<Rule Id="SA1411"
|
||||
Action="Warning"/> <!-- Attribute constructor should not use unnecessary parenthesis -->
|
||||
<Rule Id="SA1412" Action="None"/> <!-- Store files as UTF-8 with byte order mark -->
|
||||
<Rule Id="SA1413" Action="None"/> <!-- Use trailing comma in multi-line initializers -->
|
||||
</Rules>
|
||||
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.LayoutRules">
|
||||
<Rule Id="SA1500" Action="None"/> <!-- Braces for multi-line statements should not share line -->
|
||||
<Rule Id="SA1501" Action="Warning"/> <!-- Statement should not be on a single line -->
|
||||
<Rule Id="SA1502" Action="None"/> <!-- Element should not be on a single line -->
|
||||
<Rule Id="SA1503" Action="None"/> <!-- Braces should not be omitted -->
|
||||
<Rule Id="SA1503" Action="Warning"/> <!-- Braces should not be omitted -->
|
||||
<Rule Id="SA1504" Action="Warning"/> <!-- All accessors should be single-line or multi-line -->
|
||||
<Rule Id="SA1505" Action="Warning"/> <!-- Opening braces should not be followed by blank line -->
|
||||
<Rule Id="SA1506" Action="Warning"/> <!-- Element documentation headers should not be followed by blank line -->
|
||||
<Rule Id="SA1506"
|
||||
Action="Warning"/> <!-- Element documentation headers should not be followed by blank line -->
|
||||
<Rule Id="SA1507" Action="Warning"/> <!-- Code should not contain multiple blank lines in a row -->
|
||||
<Rule Id="SA1508" Action="Warning"/> <!-- Closing braces should not be preceded by blank line -->
|
||||
<Rule Id="SA1509" Action="Warning"/> <!-- Opening braces should not be preceded by blank line -->
|
||||
<Rule Id="SA1510" Action="Warning"/> <!-- Chained statement blocks should not be preceded by blank line -->
|
||||
<Rule Id="SA1510"
|
||||
Action="Warning"/> <!-- Chained statement blocks should not be preceded by blank line -->
|
||||
<Rule Id="SA1511" Action="Warning"/> <!-- While-do footer should not be preceded by blank line -->
|
||||
<Rule Id="SA1512" Action="Warning"/> <!-- Single-line comments should not be followed by blank line -->
|
||||
<Rule Id="SA1513" Action="Warning"/> <!-- Closing brace should be followed by blank line -->
|
||||
<Rule Id="SA1514" Action="Warning"/> <!-- Element documentation header should be preceded by blank line -->
|
||||
<Rule Id="SA1514"
|
||||
Action="Warning"/> <!-- Element documentation header should be preceded by blank line -->
|
||||
<Rule Id="SA1515" Action="Warning"/> <!-- Single-line comment should be preceded by blank line -->
|
||||
<Rule Id="SA1516" Action="Warning"/> <!-- Elements should be separated by blank line -->
|
||||
<Rule Id="SA1517" Action="Warning"/> <!-- Code should not contain blank lines at start of file -->
|
||||
<Rule Id="SA1518" Action="Warning"/> <!-- Use line endings correctly at end of file -->
|
||||
<Rule Id="SA1519" Action="Warning"/> <!-- Braces should not be omitted from multi-line child statement -->
|
||||
<Rule Id="SA1519"
|
||||
Action="Warning"/> <!-- Braces should not be omitted from multi-line child statement -->
|
||||
<Rule Id="SA1520" Action="Warning"/> <!-- Use braces consistently -->
|
||||
</Rules>
|
||||
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers.DocumentationRules">
|
||||
<Rule Id="SA1600" Action="None"/> <!-- Elements should be documented -->
|
||||
<Rule Id="SA1601" Action="None"/> <!-- Partial elements should be documented -->
|
||||
@ -173,27 +186,34 @@
|
||||
<Rule Id="SA1606" Action="Warning"/> <!-- Element documentation should have summary text -->
|
||||
<Rule Id="SA1607" Action="Warning"/> <!-- Partial element documentation should have summary text -->
|
||||
<Rule Id="SA1608" Action="Warning"/> <!-- Element documentation should not have default summary -->
|
||||
<Rule Id="SA1609" Action="Warning"/> <!-- Property documentation should have value -->
|
||||
<Rule Id="SA1609" Action="None"/> <!-- Property documentation should have value -->
|
||||
<Rule Id="SA1610" Action="Warning"/> <!-- Property documentation should have value text -->
|
||||
<Rule Id="SA1611" Action="None"/> <!-- Element parameters should be documented -->
|
||||
<Rule Id="SA1612" Action="Warning"/> <!-- Element parameter documentation should match element parameters -->
|
||||
<Rule Id="SA1613" Action="Warning"/> <!-- Element parameter documentation should declare parameter name -->
|
||||
<Rule Id="SA1612"
|
||||
Action="Warning"/> <!-- Element parameter documentation should match element parameters -->
|
||||
<Rule Id="SA1613"
|
||||
Action="Warning"/> <!-- Element parameter documentation should declare parameter name -->
|
||||
<Rule Id="SA1614" Action="Warning"/> <!-- Element parameter documentation should have text -->
|
||||
<Rule Id="SA1615" Action="None"/> <!-- Element return value should be documented -->
|
||||
<Rule Id="SA1616" Action="Warning"/> <!-- Element return value documentation should have text -->
|
||||
<Rule Id="SA1617" Action="Warning"/> <!-- Void return value should not be documented -->
|
||||
<Rule Id="SA1618" Action="None"/> <!-- Generic type parameters should be documented -->
|
||||
<Rule Id="SA1619" Action="None"/> <!-- Generic type parameters should be documented partial class -->
|
||||
<Rule Id="SA1620" Action="Warning"/> <!-- Generic type parameter documentation should match type parameters -->
|
||||
<Rule Id="SA1621" Action="Warning"/> <!-- Generic type parameter documentation should declare parameter name -->
|
||||
<Rule Id="SA1619"
|
||||
Action="None"/> <!-- Generic type parameters should be documented partial class -->
|
||||
<Rule Id="SA1620"
|
||||
Action="Warning"/> <!-- Generic type parameter documentation should match type parameters -->
|
||||
<Rule Id="SA1621"
|
||||
Action="Warning"/> <!-- Generic type parameter documentation should declare parameter name -->
|
||||
<Rule Id="SA1622" Action="Warning"/> <!-- Generic type parameter documentation should have text -->
|
||||
<Rule Id="SA1623" Action="Warning"/> <!-- Property summary documentation should match accessors -->
|
||||
<Rule Id="SA1624" Action="Warning"/> <!-- Property summary documentation should omit accessor with restricted access -->
|
||||
<Rule Id="SA1623" Action="None"/> <!-- Property summary documentation should match accessors -->
|
||||
<Rule Id="SA1624"
|
||||
Action="Warning"/> <!-- Property summary documentation should omit accessor with restricted access -->
|
||||
<Rule Id="SA1625" Action="Warning"/> <!-- Element documentation should not be copied and pasted -->
|
||||
<Rule Id="SA1626" Action="Warning"/> <!-- Single-line comments should not use documentation style slashes -->
|
||||
<Rule Id="SA1626"
|
||||
Action="Warning"/> <!-- Single-line comments should not use documentation style slashes -->
|
||||
<Rule Id="SA1627" Action="Warning"/> <!-- Documentation text should not be empty -->
|
||||
<Rule Id="SA1628" Action="Warning"/> <!-- Documentation text should begin with a capital letter -->
|
||||
<Rule Id="SA1629" Action="None"/> <!-- Documentation text should end with a period -->
|
||||
<Rule Id="SA1629" Action="None"/> <!-- Documentation text should end with a period -->
|
||||
<Rule Id="SA1630" Action="Warning"/> <!-- Documentation text should contain whitespace -->
|
||||
<Rule Id="SA1631" Action="Warning"/> <!-- Documentation should meet character percentage -->
|
||||
<Rule Id="SA1632" Action="Warning"/> <!-- Documentation text should meet minimum character length -->
|
||||
@ -202,12 +222,15 @@
|
||||
<Rule Id="SA1635" Action="Warning"/> <!-- File header should have copyright text -->
|
||||
<Rule Id="SA1636" Action="Warning"/> <!-- File header copyright text should match -->
|
||||
<Rule Id="SA1637" Action="Warning"/> <!-- File header should contain file name -->
|
||||
<Rule Id="SA1638" Action="Warning"/> <!-- File header file name documentation should match file name -->
|
||||
<Rule Id="SA1638"
|
||||
Action="Warning"/> <!-- File header file name documentation should match file name -->
|
||||
<Rule Id="SA1639" Action="Warning"/> <!-- File header should have summary -->
|
||||
<Rule Id="SA1640" Action="Warning"/> <!-- File header should have valid company text -->
|
||||
<Rule Id="SA1641" Action="Warning"/> <!-- File header company name text should match -->
|
||||
<Rule Id="SA1642" Action="Warning"/> <!-- Constructor summary documentation should begin with standard text -->
|
||||
<Rule Id="SA1643" Action="Warning"/> <!-- Destructor summary documentation should begin with standard text -->
|
||||
<Rule Id="SA1642"
|
||||
Action="Warning"/> <!-- Constructor summary documentation should begin with standard text -->
|
||||
<Rule Id="SA1643"
|
||||
Action="Warning"/> <!-- Destructor summary documentation should begin with standard text -->
|
||||
<Rule Id="SA1644" Action="Warning"/> <!-- Documentation headers should not contain blank lines -->
|
||||
<Rule Id="SA1645" Action="Warning"/> <!-- Included documentation file does not exist -->
|
||||
<Rule Id="SA1646" Action="Warning"/> <!-- Included documentation XPath does not exist -->
|
||||
@ -216,6 +239,5 @@
|
||||
<Rule Id="SA1649" Action="Warning"/> <!-- File name should match first type name -->
|
||||
<Rule Id="SA1650" Action="Warning"/> <!-- Element documentation should be spelled correctly -->
|
||||
<Rule Id="SA1651" Action="Warning"/> <!-- Do not use placeholder elements -->
|
||||
|
||||
</Rules>
|
||||
</RuleSet>
|
@ -1,3 +0,0 @@
|
||||
dot rbom -w
|
||||
dot trim -w
|
||||
dot tolf -w
|
1
docker/README.md
Normal file
1
docker/README.md
Normal file
@ -0,0 +1 @@
|
||||
docker
|
182
docs/CONTRIBUTING.md
Normal file
182
docs/CONTRIBUTING.md
Normal file
@ -0,0 +1,182 @@
|
||||
# 约定式提交 1.0.0
|
||||
|
||||
## [](#概述)概述
|
||||
|
||||
约定式提交规范是一种基于提交信息的轻量级约定。 它提供了一组简单规则来创建清晰的提交历史; 这更有利于编写自动化工具。 通过在提交信息中描述功能、修复和破坏性变更, 使这种惯例与 [SemVer](http://semver.org/lang/zh-CN) 相互对应。
|
||||
|
||||
提交说明的结构如下所示:
|
||||
|
||||
* * *
|
||||
|
||||
原文:
|
||||
|
||||
<type>[optional scope]: <description>
|
||||
|
||||
[optional body]
|
||||
|
||||
[optional footer(s)]
|
||||
|
||||
|
||||
译文:
|
||||
|
||||
<类型>[可选 范围]: <描述>
|
||||
|
||||
[可选 正文]
|
||||
|
||||
[可选 脚注]
|
||||
|
||||
|
||||
* * *
|
||||
|
||||
提交说明包含了下面的结构化元素,以向类库使用者表明其意图:
|
||||
|
||||
1. **fix:** _类型_ 为 `fix` 的提交表示在代码库中修复了一个 bug(这和语义化版本中的 [`PATCH`](https://semver.org/lang/zh-CN/#%E6%91%98%E8%A6%81) 相对应)。
|
||||
2. **feat:** _类型_ 为 `feat` 的提交表示在代码库中新增了一个功能(这和语义化版本中的 [`MINOR`](https://semver.org/lang/zh-CN/#%E6%91%98%E8%A6%81) 相对应)。
|
||||
3. **BREAKING CHANGE:** 在脚注中包含 `BREAKING CHANGE:` 或 <类型>(范围) 后面有一个 `!` 的提交,表示引入了破坏性 API 变更(这和语义化版本中的 [`MAJOR`](https://semver.org/lang/zh-CN/#%E6%91%98%E8%A6%81) 相对应)。 破坏性变更可以是任意 _类型_ 提交的一部分。
|
||||
4. 除 `fix:` 和 `feat:` 之外,也可以使用其它提交 _类型_ ,例如 [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional)(基于 [Angular 约定](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines))中推荐的 `build:`、`chore:`、 `ci:`、`docs:`、`style:`、`refactor:`、`perf:`、`test:`,等等。
|
||||
* build: 用于修改项目构建系统,例如修改依赖库、外部接口或者升级 Node 版本等;
|
||||
* chore: 用于对非业务性代码进行修改,例如修改构建流程或者工具配置等;
|
||||
* ci: 用于修改持续集成流程,例如修改 Travis、Jenkins 等工作流配置;
|
||||
* docs: 用于修改文档,例如修改 README 文件、API 文档等;
|
||||
* style: 用于修改代码的样式,例如调整缩进、空格、空行等;
|
||||
* refactor: 用于重构代码,例如修改代码结构、变量名、函数名等但不修改功能逻辑;
|
||||
* perf: 用于优化性能,例如提升代码的性能、减少内存占用等;
|
||||
* test: 用于修改测试用例,例如添加、删除、修改代码的测试用例等。
|
||||
5. 脚注中除了 `BREAKING CHANGE: <description>` ,其它条目应该采用类似 [git trailer format](https://git-scm.com/docs/git-interpret-trailers) 这样的惯例。
|
||||
|
||||
其它提交类型在约定式提交规范中并没有强制限制,并且在语义化版本中没有隐式影响(除非它们包含 BREAKING CHANGE)。 可以为提交类型添加一个围在圆括号内的范围,以为其提供额外的上下文信息。例如 `feat(parser): adds ability to parse arrays.`。
|
||||
|
||||
## [](#示例)示例
|
||||
|
||||
### [](#包含了描述并且脚注中有破坏性变更的提交说明)包含了描述并且脚注中有破坏性变更的提交说明
|
||||
|
||||
feat: allow provided config object to extend other configs
|
||||
|
||||
BREAKING CHANGE: `extends` key in config file is now used for extending other config files
|
||||
|
||||
|
||||
### [](#包含了--字符以提醒注意破坏性变更的提交说明)包含了 `!` 字符以提醒注意破坏性变更的提交说明
|
||||
|
||||
feat!: send an email to the customer when a product is shipped
|
||||
|
||||
|
||||
### [](#包含了范围和破坏性变更--的提交說明)包含了范围和破坏性变更 `!` 的提交說明
|
||||
|
||||
feat(api)!: send an email to the customer when a product is shipped
|
||||
|
||||
|
||||
### [](#包含了--和-breaking-change-脚注的提交说明)包含了 `!` 和 BREAKING CHANGE 脚注的提交说明
|
||||
|
||||
chore!: drop support for Node 6
|
||||
|
||||
BREAKING CHANGE: use JavaScript features not available in Node 6.
|
||||
|
||||
|
||||
### [](#不包含正文的提交说明)不包含正文的提交说明
|
||||
|
||||
docs: correct spelling of CHANGELOG
|
||||
|
||||
|
||||
### [](#包含范围的提交说明)包含范围的提交说明
|
||||
|
||||
feat(lang): add polish language
|
||||
|
||||
|
||||
### [](#包含多行正文和多行脚注的提交说明)包含多行正文和多行脚注的提交说明
|
||||
|
||||
fix: prevent racing of requests
|
||||
|
||||
Introduce a request id and a reference to latest request. Dismiss
|
||||
incoming responses other than from latest request.
|
||||
|
||||
Remove timeouts which were used to mitigate the racing issue but are
|
||||
obsolete now.
|
||||
|
||||
Reviewed-by: Z
|
||||
Refs: #123
|
||||
|
||||
|
||||
## [](#约定式提交规范)约定式提交规范
|
||||
|
||||
本文中的关键词 “必须(MUST)”、“禁止(MUST NOT)”、“必要(REQUIRED)”、“应当(SHALL)”、“不应当(SHALL NOT)”、“应该(SHOULD)”、“不应该(SHOULD NOT)”、“推荐(RECOMMENDED)”、“可以(MAY)” 和 “可选(OPTIONAL)” ,其相关解释参考 [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt) 。
|
||||
|
||||
1. 每个提交都**必须**使用类型字段前缀,它由一个名词构成,诸如 `feat` 或 `fix` , 其后接**可选的**范围字段,**可选的** `!`,以及**必要的**冒号(英文半角)和空格。
|
||||
2. 当一个提交为应用或类库实现了新功能时,**必须**使用 `feat` 类型。
|
||||
3. 当一个提交为应用修复了 bug 时,**必须**使用 `fix` 类型。
|
||||
4. 范围字段**可以**跟随在类型字段后面。范围**必须**是一个描述某部分代码的名词,并用圆括号包围,例如: `fix(parser):`
|
||||
5. 描述字段**必须**直接跟在 <类型>(范围) 前缀的冒号和空格之后。 描述指的是对代码变更的简短总结,例如: _fix: array parsing issue when multiple spaces were contained in string_ 。
|
||||
6. 在简短描述之后,**可以**编写较长的提交正文,为代码变更提供额外的上下文信息。正文**必须**起始于描述字段结束的一个空行后。
|
||||
7. 提交的正文内容自由编写,并**可以**使用空行分隔不同段落。
|
||||
8. 在正文结束的一个空行之后,**可以**编写一行或多行脚注。每行脚注都**必须**包含 一个令牌(token),后面紧跟 `:<space>` 或 `<space>#` 作为分隔符,后面再紧跟令牌的值(受 [git trailer convention](https://git-scm.com/docs/git-interpret-trailers) 启发)。
|
||||
9. 脚注的令牌**必须**使用 `-` 作为连字符,比如 `Acked-by` (这样有助于 区分脚注和多行正文)。有一种例外情况就是 `BREAKING CHANGE`,它**可以**被认为是一个令牌。
|
||||
10. 脚注的值**可以**包含空格和换行,值的解析过程**必须**直到下一个脚注的令牌/分隔符出现为止。
|
||||
11. 破坏性变更**必须**在提交信息中标记出来,要么在 <类型>(范围) 前缀中标记,要么作为脚注的一项。
|
||||
12. 包含在脚注中时,破坏性变更**必须**包含大写的文本 `BREAKING CHANGE`,后面紧跟着冒号、空格,然后是描述,例如: _BREAKING CHANGE: environment variables now take precedence over config files_ 。
|
||||
13. 包含在 <类型>(范围) 前缀时,破坏性变更**必须**通过把 `!` 直接放在 `:` 前面标记出来。 如果使用了 `!`,那么脚注中**可以**不写 `BREAKING CHANGE:`, 同时提交信息的描述中**应该**用来描述破坏性变更。
|
||||
14. 在提交说明中,**可以**使用 `feat` 和 `fix` 之外的类型,比如:_docs: updated ref docs._ 。
|
||||
15. 工具的实现必须**不区分**大小写地解析构成约定式提交的信息单元,只有 `BREAKING CHANGE` **必须**是大写的。
|
||||
16. BREAKING-CHANGE 作为脚注的令牌时**必须**是 BREAKING CHANGE 的同义词。
|
||||
|
||||
## [](#为什么使用约定式提交)为什么使用约定式提交
|
||||
|
||||
* 自动化生成 CHANGELOG。
|
||||
* 基于提交的类型,自动决定语义化的版本变更。
|
||||
* 向同事、公众与其他利益关系者传达变化的性质。
|
||||
* 触发构建和部署流程。
|
||||
* 让人们探索一个更加结构化的提交历史,以便降低对你的项目做出贡献的难度。
|
||||
|
||||
## [](#faq)FAQ
|
||||
|
||||
### [](#在初始开发阶段我该如何处理提交说明)在初始开发阶段我该如何处理提交说明?
|
||||
|
||||
我们建议你按照假设你已发布了产品那样来处理。因为通常总 _有人_ 使用你的软件,即便那是你软件开发的同事们。他们会希望知道诸如修复了什么、哪里不兼容等信息。
|
||||
|
||||
### [](#提交标题中的类型是大写还是小写)提交标题中的类型是大写还是小写?
|
||||
|
||||
大小写都可以,但最好是一致的。
|
||||
|
||||
### [](#如果提交符合多种类型我该如何操作)如果提交符合多种类型我该如何操作?
|
||||
|
||||
回退并尽可能创建多次提交。约定式提交的好处之一是能够促使我们做出更有组织的提交和 PR。
|
||||
|
||||
### [](#这不会阻碍快速开发和迭代吗)这不会阻碍快速开发和迭代吗?
|
||||
|
||||
它阻碍的是以杂乱无章的方式快速前进。它助你能在横跨多个项目以及和多个贡献者协作时长期地快速演进。
|
||||
|
||||
### [](#约定式提交会让开发者受限于提交的类型吗因为他们会想着已提供的类型)约定式提交会让开发者受限于提交的类型吗(因为他们会想着已提供的类型)?
|
||||
|
||||
约定式提交鼓励我们更多地使用某些类型的提交,比如 `fixes`。除此之外,约定式提交的灵活性也允许你的团队使用自己的类型,并随着时间的推移更改这些类型。
|
||||
|
||||
### [](#这和-semver-有什么关联呢)这和 SemVer 有什么关联呢?
|
||||
|
||||
`fix` 类型提交应当对应到 `PATCH` 版本。`feat` 类型提交应该对应到 `MINOR` 版本。带有 `BREAKING CHANGE` 的提交不管类型如何,都应该对应到 `MAJOR` 版本。
|
||||
|
||||
### [](#我对约定式提交做了形如-jameswomackconventional-commit-spec-的扩展该如何版本化管理这些扩展呢)我对约定式提交做了形如 `@jameswomack/conventional-commit-spec` 的扩展,该如何版本化管理这些扩展呢?
|
||||
|
||||
我们推荐使用 SemVer 来发布你对于这个规范的扩展(并鼓励你创建这些扩展!)
|
||||
|
||||
### [](#如果我不小心使用了错误的提交类型该怎么办呢)如果我不小心使用了错误的提交类型,该怎么办呢?
|
||||
|
||||
#### [](#当你使用了在规范中但错误的类型时例如将-feat-写成了-fix)当你使用了在规范中但错误的类型时,例如将 `feat` 写成了 `fix`
|
||||
|
||||
在合并或发布这个错误之前,我们建议使用 `git rebase -i` 来编辑提交历史。而在发布之后,根据你使用的工具和流程不同,会有不同的清理方案。
|
||||
|
||||
#### [](#当使用了-不在-规范中的类型时例如将-feat-写成了-feet)当使用了 _不在_ 规范中的类型时,例如将 `feat` 写成了 `feet`
|
||||
|
||||
在最坏的场景下,即便提交没有满足约定式提交的规范,也不会是世界末日。这只意味着这个提交会被基于规范的工具错过而已。
|
||||
|
||||
### [](#所有的贡献者都需要使用约定式提交规范吗)所有的贡献者都需要使用约定式提交规范吗?
|
||||
|
||||
并不!如果你使用基于 squash 的 Git 工作流,主管维护者可以在合并时清理提交信息——这不会对普通提交者产生额外的负担。 有种常见的工作流是让 git 系统自动从 pull request 中 squash 出提交,并向主管维护者提供一份表单,用以在合并时输入合适的 git 提交信息。
|
||||
|
||||
### [](#约定式提交规范中如何处理还原revert提交)约定式提交规范中如何处理还原(revert)提交?
|
||||
|
||||
还原提交(Reverting)会比较复杂:你还原的是多个提交吗?如果你还原了一个功能模块,下次发布的应该是补丁吗?
|
||||
|
||||
约定式提交不能明确的定义还原行为。所以我们把这个问题留给工具开发者, 基于 _类型_ 和 _脚注_ 的灵活性来开发他们自己的还原处理逻辑。
|
||||
|
||||
一种建议是使用 `revert` 类型,和一个指向被还原提交摘要的脚注:
|
||||
|
||||
revert: let us never again speak of the noodle incident
|
||||
|
||||
Refs: 676104e, a215868
|
226
docs/SEMVER.md
Normal file
226
docs/SEMVER.md
Normal file
@ -0,0 +1,226 @@
|
||||
语义化版本 2.0.0
|
||||
===
|
||||
|
||||
摘要
|
||||
---
|
||||
|
||||
版本格式:主版本号.次版本号.修订号,版本号递增规则如下:
|
||||
|
||||
1. 主版本号:当你做了不兼容的 API 修改,
|
||||
2. 次版本号:当你做了向下兼容的功能性新增,
|
||||
3. 修订号:当你做了向下兼容的问题修正。
|
||||
|
||||
先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。
|
||||
|
||||
简介
|
||||
---
|
||||
|
||||
在软件管理的领域里存在着被称作“依赖地狱”的死亡之谷,系统规模越大,加入的包越多,你就越有可能在未来的某一天发现自己已深陷绝望之中。
|
||||
|
||||
在依赖高的系统中发布新版本包可能很快会成为噩梦。如果依赖关系过高,可能面临版本控制被锁死的风险(必须对每一个依赖包改版才能完成某次升级)。而如果依赖关系过于松散,又将无法避免版本的混乱(假设兼容于未来的多个版本已超出了合理数量)。当你项目的进展因为版本依赖被锁死或版本混乱变得不够简便和可靠,就意味着你正处于依赖地狱之中。
|
||||
|
||||
作为这个问题的解决方案之一,我提议用一组简单的规则及条件来约束版本号的配置和增长。这些规则是根据(但不局限于)已经被各种封闭、开放源码软件所广泛使用的惯例所设计。为了让这套理论运作,你必须先有定义好的公共 API。这可能包括文档或代码的强制要求。无论如何,这套 API 的清楚明了是十分重要的。一旦你定义了公共 API,你就可以透过修改相应的版本号来向大家说明你的修改。考虑使用这样的版本号格式:X.Y.Z(主版本号.次版本号.修订号)修复问题但不影响 API 时,递增修订号;API 保持向下兼容的新增及修改时,递增次版本号;进行不向下兼容的修改时,递增主版本号。
|
||||
|
||||
我称这套系统为“语义化的版本控制”,在这套约定下,版本号及其更新方式包含了相邻版本间的底层代码和修改内容的信息。
|
||||
|
||||
语义化版本控制规范(SemVer)
|
||||
---
|
||||
|
||||
以下关键词 MUST、MUST NOT、REQUIRED、SHALL、SHALL NOT、SHOULD、SHOULD NOT、 RECOMMENDED、MAY、OPTIONAL 依照 RFC 2119 的叙述解读。
|
||||
|
||||
1. 使用语义化版本控制的软件必须(MUST)定义公共 API。该 API 可以在代码中被定义或出现于严谨的文档内。无论何种形式都应该力求精确且完整。
|
||||
|
||||
2. 标准的版本号必须(MUST)采用 X.Y.Z 的格式,其中 X、Y 和 Z 为非负的整数,且禁止(MUST NOT)在数字前方补零。X 是主版本号、Y 是次版本号、而 Z 为修订号。每个元素必须(MUST)以数值来递增。例如:1.9.1 -> 1.10.0 -> 1.11.0。
|
||||
|
||||
3. 标记版本号的软件发行后,禁止(MUST NOT)改变该版本软件的内容。任何修改都必须(MUST)以新版本发行。
|
||||
|
||||
4. 主版本号为零(0.y.z)的软件处于开发初始阶段,一切都可能随时被改变。这样的公共 API 不应该被视为稳定版。
|
||||
|
||||
5. 1.0.0 的版本号用于界定公共 API 的形成。这一版本之后所有的版本号更新都基于公共 API 及其修改内容。
|
||||
|
||||
6. 修订号 Z(x.y.Z `|` x > 0)必须(MUST)在只做了向下兼容的修正时才递增。这里的修正指的是针对不正确结果而进行的内部修改。
|
||||
|
||||
7. 次版本号 Y(x.Y.z `|` x > 0)必须(MUST)在有向下兼容的新功能出现时递增。在任何公共 API 的功能被标记为弃用时也必须(MUST)递增。也可以(MAY)在内部程序有大量新功能或改进被加入时递增,其中可以(MAY)包括修订级别的改变。每当次版本号递增时,修订号必须(MUST)归零。
|
||||
|
||||
8. 主版本号 X(X.y.z `|` X > 0)必须(MUST)在有任何不兼容的修改被加入公共 API 时递增。其中可以(MAY)包括次版本号及修订级别的改变。每当主版本号递增时,次版本号和修订号必须(MUST)归零。
|
||||
|
||||
9. 先行版本号可以(MAY)被标注在修订版之后,先加上一个连接号再加上一连串以句点分隔的标识符来修饰。标识符必须(MUST)由 ASCII 字母数字和连接号 [0-9A-Za-z-] 组成,且禁止(MUST NOT)留白。数字型的标识符禁止(MUST NOT)在前方补零。先行版的优先级低于相关联的标准版本。被标上先行版本号则表示这个版本并非稳定而且可能无法满足预期的兼容性需求。范例:1.0.0-alpha、1.0.0-alpha.1、1.0.0-0.3.7、1.0.0-x.7.z.92。
|
||||
|
||||
10. 版本编译信息可以(MAY)被标注在修订版或先行版本号之后,先加上一个加号再加上一连串以句点分隔的标识符来修饰。标识符必须(MUST)由 ASCII 字母数字和连接号 [0-9A-Za-z-] 组成,且禁止(MUST NOT)留白。当判断版本的优先层级时,版本编译信息可(SHOULD)被忽略。因此当两个版本只有在版本编译信息有差别时,属于相同的优先层级。范例:1.0.0-alpha+001、1.0.0+20130313144700、1.0.0-beta+exp.sha.5114f85。
|
||||
|
||||
11. 版本的优先层级指的是不同版本在排序时如何比较。
|
||||
|
||||
1. 判断优先层级时,必须(MUST)把版本依序拆分为主版本号、次版本号、修订号及先行版本号后进行比较(版本编译信息不在这份比较的列表中)。
|
||||
|
||||
2. 由左到右依序比较每个标识符,第一个差异值用来决定优先层级:主版本号、次版本号及修订号以数值比较。
|
||||
|
||||
例如:1.0.0 < 2.0.0 < 2.1.0 < 2.1.1。
|
||||
|
||||
3. 当主版本号、次版本号及修订号都相同时,改以优先层级比较低的先行版本号决定。
|
||||
|
||||
例如:1.0.0-alpha < 1.0.0。
|
||||
|
||||
4. 有相同主版本号、次版本号及修订号的两个先行版本号,其优先层级必须(MUST)透过由左到右的每个被句点分隔的标识符来比较,直到找到一个差异值后决定:
|
||||
|
||||
1. 只有数字的标识符以数值高低比较。
|
||||
|
||||
2. 有字母或连接号时则逐字以 ASCII 的排序来比较。
|
||||
|
||||
3. 数字的标识符比非数字的标识符优先层级低。
|
||||
|
||||
4. 若开头的标识符都相同时,栏位比较多的先行版本号优先层级比较高。
|
||||
|
||||
例如:1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0。
|
||||
|
||||
合法语义化版本的巴科斯范式语法
|
||||
--------------------------------------------------
|
||||
```
|
||||
<valid semver> ::= <version core>
|
||||
| <version core> "-" <pre-release>
|
||||
| <version core> "+" <build>
|
||||
| <version core> "-" <pre-release> "+" <build>
|
||||
|
||||
<version core> ::= <major> "." <minor> "." <patch>
|
||||
|
||||
<major> ::= <numeric identifier>
|
||||
|
||||
<minor> ::= <numeric identifier>
|
||||
|
||||
<patch> ::= <numeric identifier>
|
||||
|
||||
<pre-release> ::= <dot-separated pre-release identifiers>
|
||||
|
||||
<dot-separated pre-release identifiers> ::= <pre-release identifier>
|
||||
| <pre-release identifier> "." <dot-separated pre-release identifiers>
|
||||
|
||||
<build> ::= <dot-separated build identifiers>
|
||||
|
||||
<dot-separated build identifiers> ::= <build identifier>
|
||||
| <build identifier> "." <dot-separated build identifiers>
|
||||
|
||||
<pre-release identifier> ::= <alphanumeric identifier>
|
||||
| <numeric identifier>
|
||||
|
||||
<build identifier> ::= <alphanumeric identifier>
|
||||
| <digits>
|
||||
|
||||
<alphanumeric identifier> ::= <non-digit>
|
||||
| <non-digit> <identifier characters>
|
||||
| <identifier characters> <non-digit>
|
||||
| <identifier characters> <non-digit> <identifier characters>
|
||||
|
||||
<numeric identifier> ::= "0"
|
||||
| <positive digit>
|
||||
| <positive digit> <digits>
|
||||
|
||||
<identifier characters> ::= <identifier character>
|
||||
| <identifier character> <identifier characters>
|
||||
|
||||
<identifier character> ::= <digit>
|
||||
| <non-digit>
|
||||
|
||||
<non-digit> ::= <letter>
|
||||
| "-"
|
||||
|
||||
<digits> ::= <digit>
|
||||
| <digit> <digits>
|
||||
|
||||
<digit> ::= "0"
|
||||
| <positive digit>
|
||||
|
||||
<positive digit> ::= "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
|
||||
|
||||
<letter> ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J"
|
||||
| "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T"
|
||||
| "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b" | "c" | "d"
|
||||
| "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n"
|
||||
| "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x"
|
||||
| "y" | "z"
|
||||
```
|
||||
|
||||
为什么要使用语义化的版本控制?
|
||||
---
|
||||
|
||||
这并不是一个新的或者革命性的想法。实际上,你可能已经在做一些近似的事情了。问题在于只是“近似”还不够。如果没有某个正式的规范可循,版本号对于依赖的管理并无实质意义。将上述的想法命名并给予清楚的定义,让你对软件使用者传达意向变得容易。一旦这些意向变得清楚,弹性(但又不会太弹性)的依赖规范就能达成。
|
||||
|
||||
举个简单的例子就可以展示语义化的版本控制如何让依赖地狱成为过去。假设有个名为“救火车”的函数库,它需要另一个名为“梯子”并已经有使用语义化版本控制的包。当救火车创建时,梯子的版本号为 3.1.0。因为救火车使用了一些版本 3.1.0 所新增的功能,你可以放心地指定依赖于梯子的版本号大于等于 3.1.0 但小于 4.0.0。这样,当梯子版本 3.1.1 和 3.2.0 发布时,你可以将直接它们纳入你的包管理系统,因为它们能与原有依赖的软件兼容。
|
||||
|
||||
作为一位负责任的开发者,你理当确保每次包升级的运作与版本号的表述一致。现实世界是复杂的,我们除了提高警觉外能做的不多。你所能做的就是让语义化的版本控制为你提供一个健全的方式来发行以及升级包,而无需推出新的依赖包,节省你的时间及烦恼。
|
||||
|
||||
如果你对此认同,希望立即开始使用语义化版本控制,你只需声明你的函数库正在使用它并遵循这些规则就可以了。请在你的 README 文件中保留此页链接,让别人也知道这些规则并从中受益。
|
||||
|
||||
FAQ
|
||||
---
|
||||
|
||||
### 在 0.y.z 初始开发阶段,我该如何进行版本控制?
|
||||
|
||||
最简单的做法是以 0.1.0 作为你的初始化开发版本,并在后续的每次发行时递增次版本号。
|
||||
|
||||
### 如何判断发布 1.0.0 版本的时机?
|
||||
|
||||
当你的软件被用于正式环境,它应该已经达到了 1.0.0 版。如果你已经有个稳定的 API 被使用者依赖,也会是 1.0.0 版。如果你很担心向下兼容的问题,也应该算是 1.0.0 版了。
|
||||
|
||||
### 这不会阻碍快速开发和迭代吗?
|
||||
|
||||
主版本号为零的时候就是为了做快速开发。如果你每天都在改变 API,那么你应该仍在主版本号为零的阶段(0.y.z),或是正在下个主版本的独立开发分支中。
|
||||
|
||||
### 对于公共 API,若即使是最小但不向下兼容的改变都需要产生新的主版本号,岂不是很快就达到 42.0.0 版?
|
||||
|
||||
这是开发的责任感和前瞻性的问题。不兼容的改变不应该轻易被加入到有许多依赖代码的软件中。升级所付出的代价可能是巨大的。要递增主版本号来发行不兼容的改版,意味着你必须为这些改变所带来的影响深思熟虑,并且评估所涉及的成本及效益比。
|
||||
|
||||
### 为整个公共 API 写文档太费事了!
|
||||
|
||||
为供他人使用的软件编写适当的文档,是你作为一名专业开发者应尽的职责。保持项目高效的一个非常重要的部分是掌控软件的复杂度,如果没有人知道如何使用你的软件或不知道哪些函数的调用是可靠的,要掌控复杂度会是困难的。长远来看,使用语义化版本控制以及对于公共 API 有良好规范的坚持,可以让每个人及每件事都运行顺畅。
|
||||
|
||||
### 万一不小心把一个不兼容的改版当成了次版本号发行了该怎么办?
|
||||
|
||||
一旦发现自己破坏了语义化版本控制的规范,就要修正这个问题,并发行一个新的次版本号来更正这个问题并且恢复向下兼容。即使是这种情况,也不能去修改已发行的版本。可以的话,将有问题的版本号记录到文档中,告诉使用者问题所在,让他们能够意识到这是有问题的版本。
|
||||
|
||||
### 如果我更新了自己的依赖但没有改变公共 API 该怎么办?
|
||||
|
||||
由于没有影响到公共 API,这可以被认定是兼容的。若某个软件和你的包有共同依赖,则它会有自己的依赖规范,作者也会告知可能的冲突。要判断改版是属于修订等级或是次版等级,是依据你更新的依赖关系是为了修复问题或是加入新功能。对于后者,我经常会预期伴随着更多的代码,这显然会是一个次版本号级别的递增。
|
||||
|
||||
### 如果我变更了公共 API 但无意中未遵循版本号的改动怎么办呢?(意即在修订等级的发布中,误将重大且不兼容的改变加到代码之中)
|
||||
|
||||
自行做最佳的判断。如果你有庞大的使用者群在依照公共 API 的意图而变更行为后会大受影响,那么最好做一次主版本的发布,即使严格来说这个修复仅是修订等级的发布。记住, 语义化的版本控制就是透过版本号的改变来传达意义。若这些改变对你的使用者是重要的,那就透过版本号来向他们说明。
|
||||
|
||||
### 我该如何处理即将弃用的功能?
|
||||
|
||||
弃用现存的功能是软件开发中的家常便饭,也通常是向前发展所必须的。当你弃用部分公共 API 时,你应该做两件事:(1)更新你的文档让使用者知道这个改变,(2)在适当的时机将弃用的功能透过新的次版本号发布。在新的主版本完全移除弃用功能前,至少要有一个次版本包含这个弃用信息,这样使用者才能平顺地转移到新版 API。
|
||||
|
||||
### 语义化版本对于版本的字符串长度是否有限制呢?
|
||||
|
||||
没有,请自行做适当的判断。举例来说,长到 255 个字符的版本已过度夸张。再者,特定的系统对于字符串长度可能会有他们自己的限制。
|
||||
|
||||
### “v1.2.3” 是一个语义化版本号吗?
|
||||
|
||||
“v1.2.3” 并不是一个语义化的版本号。但是,在语义化版本号之前增加前缀 “v” 是用来表示版本号的常用做法。在版本控制系统中,将 “version” 缩写为 “v” 是很常见的。比如:`git tag v1.2.3 -m "Release version 1.2.3"` 中,“v1.2.3” 表示标签名称,而 “1.2.3” 是语义化版本号。
|
||||
|
||||
### 是否有推荐的正则表达式用以检查语义化版本号的正确性?
|
||||
|
||||
有两个推荐的正则表达式。第一个用于支持按组名称提取的语言(PCRE[Perl 兼容正则表达式,比如 Perl、PHP 和 R]、Python 和 Go)。
|
||||
|
||||
参见:<https://regex101.com/r/Ly7O1x/3/>
|
||||
|
||||
```
|
||||
^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
|
||||
```
|
||||
|
||||
第二个用于支持按编号提取的语言(与第一个对应的提取项按顺序分别为:major、minor、patch、prerelease、buildmetadata)。主要包括 ECMA Script(JavaScript)、PCRE(Perl 兼容正则表达式,比如 Perl、PHP 和 R)、Python 和 Go。
|
||||
参见:<https://regex101.com/r/vkijKf/1/>
|
||||
|
||||
```
|
||||
^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
|
||||
```
|
||||
|
||||
关于
|
||||
---
|
||||
|
||||
语义化版本控制的规范是由 Gravatars 创办者兼 GitHub 共同创办者 [Tom Preston-Werner](http://tom.preston-werner.com) 所建立。
|
||||
|
||||
如果您有任何建议,请到 [GitHub 上提出您的问题](https://github.com/semver/semver/issues)。
|
||||
|
||||
许可证
|
||||
---
|
||||
|
||||
[知识共享 署名 3.0 (CC BY 3.0)](http://creativecommons.org/licenses/by/3.0/)
|
@ -1,6 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-t4": {
|
||||
"version": "3.0.0",
|
||||
"commands": [
|
||||
"t4"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
git reset --hard | git clean -d -fx .
|
16
global.json
16
global.json
@ -1,10 +1,10 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "7.0.100",
|
||||
"allowPrerelease": true,
|
||||
"rollForward": "major"
|
||||
},
|
||||
"tools": {
|
||||
"dotnet": "7.0.100"
|
||||
}
|
||||
"sdk": {
|
||||
"version": "8.0.0",
|
||||
"rollForward": "latestMajor",
|
||||
"allowPrerelease": true
|
||||
},
|
||||
"tools": {
|
||||
"dotnet": "8.0.0"
|
||||
}
|
||||
}
|
7
nuget.config
Normal file
7
nuget.config
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="nuget.cdn.azure.cn" value="https://nuget.cdn.azure.cn/v3/index.json" />
|
||||
</packageSources>
|
||||
</configuration>
|
14
package.json
Normal file
14
package.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"version": "2.3.5",
|
||||
"devDependencies": {
|
||||
"cz-git": "^1.11.0",
|
||||
"commitizen": "^4.3.1",
|
||||
"prettier": "^3.3.3",
|
||||
"standard-version": "^9.5.0"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "node_modules/cz-git"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
Param(
|
||||
# Nuget APIKey
|
||||
[string] $apikey
|
||||
)
|
||||
|
||||
if ($apikey -eq $null -or $apikey -eq "")
|
||||
{
|
||||
Write-Error "require apiKey";
|
||||
return;
|
||||
}
|
||||
|
||||
rm -r ./build/nupkgs
|
||||
dotnet build -c Release
|
||||
$files = Get-ChildItem -Path ./build/nupkgs/ -Filter *.nupkg
|
||||
foreach ($file in $files)
|
||||
{
|
||||
dotnet nuget push $file.fullName --skip-duplicate --api-key $apikey --source https://api.nuget.org/v3/index.json
|
||||
nuget add $file.fullName -source d:\nuget-pkg
|
||||
}
|
||||
$files = Get-ChildItem -Path ./build/nupkgs/ -Filter *.snupkg
|
||||
foreach ($file in $files)
|
||||
{
|
||||
dotnet nuget push $file.fullName --skip-duplicate --api-key $apikey --source https://api.nuget.org/v3/index.json
|
||||
nuget add $file.fullName -source d:\nuget-pkg
|
||||
}
|
15
scripts/1.git.pull.request.ps1
Normal file
15
scripts/1.git.pull.request.ps1
Normal file
@ -0,0 +1,15 @@
|
||||
$branch = $( git branch --show-current )
|
||||
git add ../
|
||||
$skipFormat = Read-Host "输入 n 跳过代码整理"
|
||||
if ($skipFormat -ne "n")
|
||||
{
|
||||
./code.clean.ps1
|
||||
}
|
||||
git add ../
|
||||
../node_modules/.bin/git-cz.ps1
|
||||
git pull
|
||||
git push --set-upstream origin $branch
|
||||
Start-Process -FilePath "https://github.com/nsnail/NSExt/compare/main...$branch"
|
||||
Write-Host "按『Enter』重建分支,『Ctrl+C』退出"
|
||||
Pause
|
||||
./3.git.recreate.branch.ps1
|
29
scripts/2.git.release.ps1
Normal file
29
scripts/2.git.release.ps1
Normal file
@ -0,0 +1,29 @@
|
||||
cd ..
|
||||
$types = @{
|
||||
'1' = @('major', '主版本')
|
||||
'2' = @('minor', '次版本')
|
||||
'3' = @('patch', '修订版本')
|
||||
}
|
||||
$prefix = ''
|
||||
while ($null -eq $types[$prefix])
|
||||
{
|
||||
$prefix = Read-Host "请选择版本类型`n" $( & { param($i) $i | ForEach-Object { "$_ : $( $types[$_][0] )($( $types[$_][1] ))`n" } } $types.Keys | Sort-Object )
|
||||
}
|
||||
git checkout main
|
||||
git pull
|
||||
git branch -D release
|
||||
git checkout -b release
|
||||
./node_modules/.bin/standard-version -r $types[$prefix][0]
|
||||
git commit --amend --no-edit -a
|
||||
$tag = $( git describe --tags $( git rev-list --tags --max-count = 1 ) )
|
||||
git tag -d $tag
|
||||
git tag $tag
|
||||
git push --tags origin release
|
||||
Start-Process -FilePath "https://github.com/nsnail/NSExt/compare/main...release"
|
||||
Write-Host "按『Enter』回到tk分支,『Ctrl+C』退出"
|
||||
Pause
|
||||
git checkout main
|
||||
git pull
|
||||
git branch -D release
|
||||
git branch -D tk
|
||||
git checkout -b tk
|
6
scripts/3.git.recreate.branch.ps1
Normal file
6
scripts/3.git.recreate.branch.ps1
Normal file
@ -0,0 +1,6 @@
|
||||
$branch = $( git branch --show-current )
|
||||
git checkout main
|
||||
git pull
|
||||
git branch -D $branch
|
||||
git branch $branch
|
||||
git checkout $branch
|
2
scripts/4.git.del.obsolete.tags.ps1
Normal file
2
scripts/4.git.del.obsolete.tags.ps1
Normal file
@ -0,0 +1,2 @@
|
||||
git push origin :refs/tags/$(git tag -l "*-*")
|
||||
git tag -d $(git tag -l "*-*")
|
26
scripts/clean.ln.csx
Normal file
26
scripts/clean.ln.csx
Normal file
@ -0,0 +1,26 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
var csFiles = Directory.EnumerateFiles(@"../src/backend", $"*.cs", new EnumerationOptions { RecurseSubdirectories = true });
|
||||
foreach (var lnFile in Directory.EnumerateFiles("../assets/res", "*.ln"))
|
||||
{
|
||||
var newLines = new List<string>();
|
||||
foreach (var line in File.ReadAllLines(lnFile))
|
||||
{
|
||||
var found = false;
|
||||
foreach (var csFile in csFiles)
|
||||
{
|
||||
if (File.ReadAllText(csFile).Contains($"Ln.{(Regex.IsMatch(line, @"^\d") ? "_" : "") + line}"))
|
||||
{
|
||||
found = true;
|
||||
newLines.Add(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
}
|
||||
File.WriteAllLines(lnFile, newLines);
|
||||
}
|
44
scripts/code.clean.csx
Normal file
44
scripts/code.clean.csx
Normal file
@ -0,0 +1,44 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
|
||||
{
|
||||
var files = string.Join(
|
||||
';',
|
||||
Args[0]
|
||||
.Split('\n', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Replace('\\', '/').Trim())
|
||||
);
|
||||
|
||||
Console.WriteLine(files);
|
||||
|
||||
using var p = Process.Start(
|
||||
new ProcessStartInfo
|
||||
{
|
||||
CreateNoWindow = true,
|
||||
FileName = "dotnet",
|
||||
Arguments = $"jb cleanupcode --include=\"{files}\" --no-build ../NSExt.sln",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true
|
||||
}
|
||||
);
|
||||
p.WaitForExit();
|
||||
Console.WriteLine(p.StandardOutput.ReadToEnd());
|
||||
|
||||
using var p2 = Process.Start(
|
||||
new ProcessStartInfo
|
||||
{
|
||||
CreateNoWindow = true,
|
||||
FileName = "git",
|
||||
Arguments = $"status",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true
|
||||
}
|
||||
);
|
||||
p2.WaitForExit();
|
||||
var content = p2.StandardOutput.ReadToEnd();
|
||||
Console.WriteLine(content);
|
||||
|
||||
return content.Contains("working tree clean") ? 0 : 1;
|
||||
}
|
4
scripts/code.clean.ps1
Normal file
4
scripts/code.clean.ps1
Normal file
@ -0,0 +1,4 @@
|
||||
jb cleanupcode --no-build --include = $( $( git status --porcelain | Where-Object { $_ -match "^\s*[MA]" } | ForEach-Object { $_.TrimStart(" M").TrimStart(" A") } ) -join ";" ) ../NSExt.sln
|
||||
dot rbom -w -e refs -e .git -e node_modules ../
|
||||
dot trim -w -e refs -e .git -e node_modules ../
|
||||
dot tolf -w -e refs -e .git -e node_modules ../
|
15
scripts/find.unused.ln.csx
Normal file
15
scripts/find.unused.ln.csx
Normal file
@ -0,0 +1,15 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
Console.WriteLine(string.Join(Environment.NewLine
|
||||
, Regex
|
||||
.Matches(File.ReadAllText(@"../assets/res/Ln.resx")
|
||||
, "data name=\"(.*?)\"")
|
||||
.Select(x => x.Groups[1].Value)
|
||||
.Where(x => !Directory
|
||||
.GetFiles(@"../src/backend/", "*.cs"
|
||||
, new EnumerationOptions {
|
||||
RecurseSubdirectories = true
|
||||
})
|
||||
.Select(File.ReadAllText)
|
||||
.Any(y => y.Contains(x)))));
|
||||
Console.ReadKey();
|
78
scripts/gen.cs.tt
Normal file
78
scripts/gen.cs.tt
Normal file
@ -0,0 +1,78 @@
|
||||
<#@ template language="C#" #>
|
||||
<#@ assembly name="System.Xml" #>
|
||||
<#@ output encoding="utf-8" extension="Designer.cs" #>
|
||||
<#@ import namespace="System.Xml" #>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace NSExt.Languages;
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[DebuggerNonUserCode]
|
||||
[CompilerGenerated]
|
||||
public sealed class Ln
|
||||
{
|
||||
private static ResourceManager _resourceMan;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Ln" /> class.
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
public Ln() { }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
public static ResourceManager ResourceManager {
|
||||
get {
|
||||
if (ReferenceEquals(_resourceMan, null)) {
|
||||
var temp = new ResourceManager("NSExt.Languages.Ln", typeof(Ln).Assembly);
|
||||
_resourceMan = temp;
|
||||
}
|
||||
|
||||
return _resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
public static CultureInfo Culture { get; set; }
|
||||
<#
|
||||
var xml = new XmlDocument();
|
||||
xml.Load("../assets/res/Ln.resx");
|
||||
foreach (XmlNode data in xml.SelectNodes("//root/data")!)
|
||||
{
|
||||
#>
|
||||
/// <summary>
|
||||
/// <#= data.SelectSingleNode("value")?.InnerText #>
|
||||
/// </summary>
|
||||
public static string <#=
|
||||
data.Attributes!["name"].Value.Replace(" ", "_") #> => ResourceManager.GetString("<#= data.Attributes!["name"].Value #>", Culture);
|
||||
<#
|
||||
}
|
||||
#>
|
||||
}
|
2
scripts/gen.ln.cmd
Normal file
2
scripts/gen.ln.cmd
Normal file
@ -0,0 +1,2 @@
|
||||
dotnet t4 ./gen.resx.tt -o ../assets/res/Ln.resx
|
||||
dotnet t4 ./gen.cs.tt -o ../dist/backend/NSExt/Ln.cs
|
39
scripts/gen.resx.tt
Normal file
39
scripts/gen.resx.tt
Normal file
@ -0,0 +1,39 @@
|
||||
<#@ template language="C#" #>
|
||||
<#@ output encoding="utf-8" extension="resx" #>
|
||||
<#@ import namespace="System.IO" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ import namespace="System.Text.RegularExpressions" #>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
|
||||
id="root"
|
||||
xmlns="">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</resheader>
|
||||
<#
|
||||
var regex = new Regex(@"^\d", RegexOptions.Compiled);
|
||||
foreach (var line in Directory.GetFiles("../assets/res/", "*.ln").SelectMany(x => File.ReadLines(x)).Distinct())
|
||||
{
|
||||
#>
|
||||
<data name="<#= regex.IsMatch(line) ? "_" : "" #><#= line #>" xml:space="preserve"><value><#= line #></value></data>
|
||||
<#
|
||||
}
|
||||
#>
|
||||
</root>
|
65
scripts/image.optimize.csx
Normal file
65
scripts/image.optimize.csx
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
for %%i in (*.png) do pngquant %%i --force --output %%i --skip-if-larger
|
||||
for %%i in (*.jpg) do jpegtran -copy none -optimize -perfect %%i %%i
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
var files = Directory
|
||||
.EnumerateFiles(
|
||||
"../",
|
||||
"*.png",
|
||||
new EnumerationOptions
|
||||
{
|
||||
RecurseSubdirectories = true,
|
||||
AttributesToSkip = FileAttributes.ReparsePoint,
|
||||
IgnoreInaccessible = true
|
||||
}
|
||||
)
|
||||
.ToArray();
|
||||
|
||||
Parallel.ForEach(
|
||||
files,
|
||||
file =>
|
||||
{
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "pngquant",
|
||||
Arguments = $"\"{file}\" --force --output \"{file}\" --skip-if-larger"
|
||||
};
|
||||
using var p = Process.Start(startInfo);
|
||||
p.WaitForExit();
|
||||
Console.WriteLine($"{file}: {p.ExitCode}");
|
||||
}
|
||||
);
|
||||
|
||||
files = new[] { "*.jpg", "*.jpeg" }
|
||||
.SelectMany(
|
||||
x =>
|
||||
Directory.EnumerateFiles(
|
||||
"../",
|
||||
x,
|
||||
new EnumerationOptions
|
||||
{
|
||||
RecurseSubdirectories = true,
|
||||
AttributesToSkip = FileAttributes.ReparsePoint,
|
||||
IgnoreInaccessible = true
|
||||
}
|
||||
)
|
||||
)
|
||||
.ToArray();
|
||||
|
||||
Parallel.ForEach(
|
||||
files,
|
||||
file =>
|
||||
{
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "jpegtran",
|
||||
Arguments = $"-copy none -optimize -perfect \"{file}\" \"{file}\""
|
||||
};
|
||||
using var p = Process.Start(startInfo);
|
||||
p.WaitForExit();
|
||||
Console.WriteLine($"{file}: {p.ExitCode}");
|
||||
}
|
||||
);
|
2
scripts/install.as.tpl.ps1
Normal file
2
scripts/install.as.tpl.ps1
Normal file
@ -0,0 +1,2 @@
|
||||
dotnet new uninstall ../
|
||||
dotnet new --install ../
|
27
scripts/rename.csx
Normal file
27
scripts/rename.csx
Normal file
@ -0,0 +1,27 @@
|
||||
#r "nuget: NSExt, 2.2.0"
|
||||
using NSExt.Extensions;
|
||||
|
||||
Console.WriteLine("请输入原始名称(NSExt):");
|
||||
var oldName = Console.ReadLine().NullOrEmpty("NSExt");
|
||||
Console.WriteLine("请输入替换名称:");
|
||||
var newName = Console.ReadLine();
|
||||
foreach (var path in Directory.EnumerateDirectories("../", $"*{oldName}*",
|
||||
SearchOption.AllDirectories))
|
||||
{
|
||||
Console.Write($"{path} --> ");
|
||||
var newPath = path.Replace(oldName, newName);
|
||||
Directory.Move(path, newPath);
|
||||
Console.WriteLine(newPath);
|
||||
}
|
||||
|
||||
|
||||
Console.WriteLine();
|
||||
foreach (var path in Directory.EnumerateFiles("../", $"*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
File.WriteAllText(path, File.ReadAllText(path).Replace(oldName, newName));
|
||||
var newPath = path.Replace(oldName, newName);
|
||||
if (newPath == path) continue;
|
||||
Console.Write($"{path} --> ");
|
||||
Directory.Move(path, newPath);
|
||||
Console.WriteLine(newPath);
|
||||
}
|
1
scripts/resharper.full.ps1
Normal file
1
scripts/resharper.full.ps1
Normal file
@ -0,0 +1 @@
|
||||
jb cleanupcode --no-build ../NSExt.sln
|
42
scripts/switch.nuget.or.project.csx
Normal file
42
scripts/switch.nuget.or.project.csx
Normal file
@ -0,0 +1,42 @@
|
||||
using System.Text.RegularExpressions;
|
||||
string input = string.Empty;
|
||||
while (!new[] { "1", "2" }.Contains(input))
|
||||
{
|
||||
Console.WriteLine("1.nuget 2.project");
|
||||
input = Console.ReadLine();
|
||||
}
|
||||
var slnFile = Directory.GetFiles(@"../", "*.sln").First();
|
||||
var csprojFiles = Directory.GetFiles(@"../src", "*.csproj", new EnumerationOptions { RecurseSubdirectories = true });
|
||||
var slnContent = File.ReadAllText(slnFile);
|
||||
|
||||
if (input == "1")
|
||||
{
|
||||
slnContent = Regex.Replace(slnContent, "\\nProject\\((.*)#refs", "\n##Project($1#refs");
|
||||
slnContent = Regex.Replace(slnContent, "\\nEndProject#refs", "\n##EndProject#refs");
|
||||
foreach (Match m in Regex.Matches(slnContent, "\"(\\{[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}\\})\"#refs"))
|
||||
{
|
||||
slnContent = slnContent.Replace($" {m.Groups[1].Value}.", $" ##{m.Groups[1].Value}.");
|
||||
}
|
||||
foreach (var csprojFile in csprojFiles)
|
||||
{
|
||||
var csprojContent = File.ReadAllText(csprojFile);
|
||||
csprojContent = Regex.Replace(csprojContent," <ProjectReference(.*)Label=\"refs\"(.*)>", " <!--<ProjectReference$1Label=\"refs\"$2>-->");
|
||||
csprojContent = Regex.Replace(csprojContent," <!--<PackageReference(.*)Label=\"refs\"(.*)>-->", " <PackageReference$1Label=\"refs\"$2>");
|
||||
File.WriteAllText(csprojFile, csprojContent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
slnContent = Regex.Replace(slnContent, "##", "");
|
||||
foreach (var csprojFile in csprojFiles)
|
||||
{
|
||||
var csprojContent = File.ReadAllText(csprojFile);
|
||||
csprojContent = Regex.Replace(csprojContent," <!--<ProjectReference(.*)Label=\"refs\"(.*)>-->", " <ProjectReference$1Label=\"refs\"$2>");
|
||||
csprojContent = Regex.Replace(csprojContent," <PackageReference(.*)Label=\"refs\"(.*)>", " <!--<PackageReference$1Label=\"refs\"$2>-->");
|
||||
File.WriteAllText(csprojFile, csprojContent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Console.WriteLine(slnContent);
|
||||
File.WriteAllText(slnFile, slnContent);
|
63
scripts/sync.sln.files.csx
Normal file
63
scripts/sync.sln.files.csx
Normal file
@ -0,0 +1,63 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
var slnFile = Directory.GetFiles(@"../", "*.sln").First();
|
||||
var content = File.ReadAllText(slnFile);
|
||||
|
||||
content = Regex.Replace(
|
||||
content,
|
||||
"Project\\(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\"\\) = \"meta\", \"meta\", \"{5198A03D-0CAC-4828-A807-34A693F73859}\"(?:.|\n)*?EndProject",
|
||||
$$"""
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "meta", "meta", "{5198A03D-0CAC-4828-A807-34A693F73859}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
{{string.Join('\n',
|
||||
Directory.GetFiles(@"../", "*").Where(x => !x.EndsWith(".sln") && !x.EndsWith(".user"))
|
||||
.Select(x=>$" {Path.GetFileName(x)} = {Path.GetFileName(x)}")
|
||||
)}}
|
||||
EndProject
|
||||
"""
|
||||
);
|
||||
|
||||
content = Regex.Replace(
|
||||
content,
|
||||
"Project\\(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\"\\) = \"workflows\", \"workflows\", \"{3C6F049E-3EE8-4D66-9AFF-E8A369032487}\"(?:.|\n)*?EndProject",
|
||||
$$"""
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{3C6F049E-3EE8-4D66-9AFF-E8A369032487}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
{{string.Join('\n',
|
||||
Directory.GetFiles(@"../.github/workflows", "*")
|
||||
.Select(x=>$" {Path.GetFileName(x)} = .github/workflows/{Path.GetFileName(x)}")
|
||||
)}}
|
||||
EndProject
|
||||
"""
|
||||
);
|
||||
|
||||
content = Regex.Replace(
|
||||
content,
|
||||
"Project\\(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\"\\) = \"scripts\", \"scripts\", \"{BB0B25C9-0901-4923-913F-00F9A6B352A5}\"(?:.|\n)*?EndProject",
|
||||
$$"""
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{BB0B25C9-0901-4923-913F-00F9A6B352A5}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
{{string.Join('\n',
|
||||
Directory.GetFiles(@"../scripts", "*")
|
||||
.Select(x=>$" {Path.GetFileName(x)} = scripts/{Path.GetFileName(x)}")
|
||||
)}}
|
||||
EndProject
|
||||
"""
|
||||
);
|
||||
|
||||
content = Regex.Replace(
|
||||
content,
|
||||
"Project\\(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\"\\) = \"build\", \"build\", \"{8E4C93BA-9493-4892-80C4-5E174C504829}\"(?:.|\n)*?EndProject",
|
||||
$$"""
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{8E4C93BA-9493-4892-80C4-5E174C504829}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
{{string.Join('\n',
|
||||
Directory.GetFiles(@"../build", "*")
|
||||
.Select(x=>$" {Path.GetFileName(x)} = build/{Path.GetFileName(x)}")
|
||||
)}}
|
||||
EndProject
|
||||
"""
|
||||
);
|
||||
|
||||
Console.WriteLine(content);
|
||||
File.WriteAllText(slnFile, content);
|
@ -1,29 +0,0 @@
|
||||
using System.Reflection;
|
||||
using NSExt.Attributes;
|
||||
|
||||
namespace NSExt.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// EnumExtensions
|
||||
/// </summary>
|
||||
public static class EnumExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取枚举的description属性
|
||||
/// </summary>
|
||||
/// <param name="e">枚举对象</param>
|
||||
/// <returns>description属性</returns>
|
||||
public static string Desc(this Enum e)
|
||||
{
|
||||
var t = e.GetType();
|
||||
var fi = t.GetField(Enum.GetName(t, e)!);
|
||||
var descAttr = fi!.GetCustomAttribute<DescriptionAttribute>(false);
|
||||
if (descAttr is null) {
|
||||
return Enum.GetName(t, e);
|
||||
}
|
||||
|
||||
var str = descAttr.Description;
|
||||
var locAttr = fi!.GetCustomAttribute<LocalizationAttribute>(false);
|
||||
return locAttr is null ? str : locAttr.ResourceClass.GetProperty(str)?.GetValue(default) as string ?? str;
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
namespace NSExt.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// EnumerableExtensions
|
||||
/// </summary>
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 将列表转成分隔符分隔的字符串
|
||||
/// </summary>
|
||||
public static string Join(this IEnumerable<object> me, string separator)
|
||||
{
|
||||
return string.Join(separator, me);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断对象是否为null或不存在子元素(如果为集合对象)
|
||||
/// </summary>
|
||||
/// <typeparam name="T">对象类型</typeparam>
|
||||
/// <param name="me">指定对象</param>
|
||||
/// <returns>空则返回true</returns>
|
||||
public static bool NullOrEmpty<T>(this IEnumerable<T> me)
|
||||
{
|
||||
return me is null || !me.Any();
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace NSExt.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// JsonSerializerOptionsExtensions
|
||||
/// </summary>
|
||||
public static class JsonSerializerOptionsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// NewJsonSerializerOptions
|
||||
/// </summary>
|
||||
public static JsonSerializerOptions NewJsonSerializerOptions(this JsonSerializerOptions _)
|
||||
{
|
||||
return new JsonSerializerOptions {
|
||||
ReadCommentHandling = JsonCommentHandling.Skip
|
||||
, AllowTrailingCommas = true
|
||||
, DictionaryKeyPolicy = JsonNamingPolicy.CamelCase
|
||||
, PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
, NumberHandling = JsonNumberHandling.AllowReadingFromString
|
||||
, PropertyNameCaseInsensitive = true
|
||||
};
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
// ReSharper disable TemplateIsNotCompileTimeConstantProblem
|
||||
|
||||
namespace NSExt.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// LoggerExtensions
|
||||
/// </summary>
|
||||
public static class LoggerExtensions
|
||||
{
|
||||
private const string _MESSAGE_S_THREADID_CALLERNAME_CALLERFILEPATH_CALLERLINENUMBER
|
||||
= "{Message} <s:{ThreadId}#{CallerName}@{CallerFilePath}:{CallerLineNumber}>";
|
||||
|
||||
private static readonly Action<ILogger, string, string, string, string, string, Exception> _logDebug
|
||||
= LoggerMessage.Define<string, string, string, string, string>(LogLevel.Debug, default
|
||||
, _MESSAGE_S_THREADID_CALLERNAME_CALLERFILEPATH_CALLERLINENUMBER);
|
||||
|
||||
private static readonly Action<ILogger, string, string, string, string, string, Exception> _logError
|
||||
= LoggerMessage.Define<string, string, string, string, string>(LogLevel.Error, default
|
||||
, _MESSAGE_S_THREADID_CALLERNAME_CALLERFILEPATH_CALLERLINENUMBER);
|
||||
|
||||
private static readonly Action<ILogger, string, string, string, string, string, Exception> _logFatal
|
||||
= LoggerMessage.Define<string, string, string, string, string>(LogLevel.Critical, default
|
||||
, _MESSAGE_S_THREADID_CALLERNAME_CALLERFILEPATH_CALLERLINENUMBER);
|
||||
|
||||
private static readonly Action<ILogger, string, string, string, string, string, Exception> _logInfo
|
||||
= LoggerMessage.Define<string, string, string, string, string>(LogLevel.Information, default
|
||||
, _MESSAGE_S_THREADID_CALLERNAME_CALLERFILEPATH_CALLERLINENUMBER);
|
||||
|
||||
private static readonly Action<ILogger, string, string, string, string, string, Exception> _logWarn
|
||||
= LoggerMessage.Define<string, string, string, string, string>(LogLevel.Warning, default
|
||||
, _MESSAGE_S_THREADID_CALLERNAME_CALLERFILEPATH_CALLERLINENUMBER);
|
||||
|
||||
/// <summary>
|
||||
/// Debug
|
||||
/// </summary>
|
||||
public static void Debug(this ILogger me, object message, [CallerMemberName] string callerName = null
|
||||
, [CallerFilePath] string callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
_logDebug(me, message.ToString(), Environment.CurrentManagedThreadId.ToString(CultureInfo.InvariantCulture)
|
||||
, callerName, Path.GetFileName(callerFilePath), callerLineNumber.ToString(CultureInfo.InvariantCulture)
|
||||
, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error
|
||||
/// </summary>
|
||||
public static void Error(this ILogger me, object message, [CallerMemberName] string callerName = null
|
||||
, [CallerFilePath] string callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
_logError(me, message.ToString(), Environment.CurrentManagedThreadId.ToString(CultureInfo.InvariantCulture)
|
||||
, callerName, Path.GetFileName(callerFilePath), callerLineNumber.ToString(CultureInfo.InvariantCulture)
|
||||
, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fatal
|
||||
/// </summary>
|
||||
public static void Fatal(this ILogger me, object message, Exception ex = null
|
||||
, [CallerMemberName] string callerName = null, [CallerFilePath] string callerFilePath = null
|
||||
, [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
_logFatal(me, message.ToString(), Environment.CurrentManagedThreadId.ToString(CultureInfo.InvariantCulture)
|
||||
, callerName, Path.GetFileName(callerFilePath), callerLineNumber.ToString(CultureInfo.InvariantCulture)
|
||||
, ex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Info
|
||||
/// </summary>
|
||||
public static void Info(this ILogger me, object message, [CallerMemberName] string callerName = null
|
||||
, [CallerFilePath] string callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
_logInfo(me, message.ToString(), Environment.CurrentManagedThreadId.ToString(CultureInfo.InvariantCulture)
|
||||
, callerName, Path.GetFileName(callerFilePath), callerLineNumber.ToString(CultureInfo.InvariantCulture)
|
||||
, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Warn
|
||||
/// </summary>
|
||||
public static void Warn(this ILogger me, object message, [CallerMemberName] string callerName = null
|
||||
, [CallerFilePath] string callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
_logWarn(me, message.ToString(), Environment.CurrentManagedThreadId.ToString(CultureInfo.InvariantCulture)
|
||||
, callerName, Path.GetFileName(callerFilePath), callerLineNumber.ToString(CultureInfo.InvariantCulture)
|
||||
, null);
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
namespace NSExt.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// LongExtensions
|
||||
/// </summary>
|
||||
public static class LongExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 判断枚举是否包含某个位
|
||||
/// </summary>
|
||||
public static bool HasFlag<T>(this long me, T flag)
|
||||
where T : Enum
|
||||
{
|
||||
var val = (long)(object)flag;
|
||||
return (me & val) == val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 1970毫秒数转换成日期对象
|
||||
/// </summary>
|
||||
public static DateTime Time(this long msFrom1970)
|
||||
{
|
||||
return new DateTime(1970, 1, 1).AddMilliseconds(msFrom1970).ToLocalTime();
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace NSExt.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// ObjectExtensions
|
||||
/// </summary>
|
||||
public static class ObjectExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 将一个对象序列化成json文本
|
||||
/// </summary>
|
||||
/// <param name="me">指定对象</param>
|
||||
/// <param name="format">是否格式化</param>
|
||||
/// <returns>json文本</returns>
|
||||
public static string Json(this object me, bool format = false)
|
||||
{
|
||||
return JsonSerializer.Serialize(
|
||||
me, new JsonSerializerOptions { WriteIndented = format, PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
global using System.ComponentModel;
|
||||
global using System.Data;
|
||||
global using System.Data.Common;
|
||||
global using System.Globalization;
|
||||
global using System.Runtime.CompilerServices;
|
||||
global using System.Text;
|
||||
global using System.Text.RegularExpressions;
|
||||
global using System.Web;
|
||||
global using Microsoft.Extensions.Logging;
|
@ -1,17 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
|
||||
<PackageReference Include="MinVer" Version="4.3.0-beta.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="../CodeQuality.props" />
|
||||
</Project>
|
7
src/backend/GlobalUsings.cs
Normal file
7
src/backend/GlobalUsings.cs
Normal file
@ -0,0 +1,7 @@
|
||||
global using System;
|
||||
global using System.ComponentModel;
|
||||
global using System.Diagnostics;
|
||||
global using System.Globalization;
|
||||
global using System.Text;
|
||||
global using System.Text.RegularExpressions;
|
||||
global using NSExt.Languages;
|
19
src/backend/NSExt.Tests/AllTests.cs
Normal file
19
src/backend/NSExt.Tests/AllTests.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using NSExt.Extensions;
|
||||
using Xunit;
|
||||
|
||||
namespace NSExt;
|
||||
|
||||
/// <summary>
|
||||
/// 所有测试用例
|
||||
/// </summary>
|
||||
public class AllTests
|
||||
{
|
||||
/// <summary>
|
||||
/// IsJsonString
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void IsJsonString()
|
||||
{
|
||||
Assert.True("{\"a\":\"b\"}".IsJsonString());
|
||||
}
|
||||
}
|
15
src/backend/NSExt.Tests/NSExt.Tests.csproj
Normal file
15
src/backend/NSExt.Tests/NSExt.Tests.csproj
Normal file
@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.0-preview-25107-01"/>
|
||||
<PackageReference Include="xunit" Version="2.9.3"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.4">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<ProjectReference Include="../NSExt/NSExt.csproj"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -4,12 +4,14 @@ namespace NSExt.Attributes;
|
||||
/// 指定本地化资源类型
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Field)]
|
||||
public class LocalizationAttribute : Attribute
|
||||
public sealed class LocalizationAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalizationAttribute" /> class.
|
||||
/// </summary>
|
||||
#pragma warning disable IDE0290
|
||||
public LocalizationAttribute(Type resourceClass)
|
||||
#pragma warning restore IDE0290
|
||||
{
|
||||
ResourceClass = resourceClass;
|
||||
}
|
28
src/backend/NSExt/Attributes/ResourceDescriptionAttribute.cs
Normal file
28
src/backend/NSExt/Attributes/ResourceDescriptionAttribute.cs
Normal file
@ -0,0 +1,28 @@
|
||||
namespace NSExt.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// 本地化资源描述特性
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Field)]
|
||||
public sealed class ResourceDescriptionAttribute<T> : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ResourceDescriptionAttribute{T}" /> class.
|
||||
/// </summary>
|
||||
#pragma warning disable IDE0290
|
||||
public ResourceDescriptionAttribute(string resourceName)
|
||||
#pragma warning restore IDE0290
|
||||
{
|
||||
ResourceName = resourceName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源名称
|
||||
/// </summary>
|
||||
public string ResourceName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源对象
|
||||
/// </summary>
|
||||
public T ResourceObject { get; set; }
|
||||
}
|
@ -1,19 +1,21 @@
|
||||
namespace NSExt.Constant;
|
||||
#pragma warning disable SYSLIB1045
|
||||
|
||||
// 使用 RegexGenerator 新特性会生成重复key值的xmlcomment导致出错
|
||||
/// <summary>
|
||||
/// 使用 RegexGenerator 新特性会生成重复key值的xmlComment导致出错
|
||||
/// </summary>
|
||||
internal static class Regexes
|
||||
{
|
||||
public static readonly Regex RegexBacksLantUnicode
|
||||
= new("\\\\u([a-fA-F0-9]{4})", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
= new(@"\\u([a-fA-F0-9]{4})", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public static readonly Regex RegexHtmlTag = new("<[^>]*>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public static readonly Regex RegexMobile
|
||||
= new("^(\\d{3})\\d{4}(\\d{4})$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
= new(@"^(\d{3})\d{4}(\d{4})$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public static readonly Regex RegexPercentUnicode
|
||||
= new("\\\\u([a-fA-F0-9]{4})", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
= new(@"\\u([a-fA-F0-9]{4})", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public static readonly Regex RegexUpLetter = new("([A-Z])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
}
|
@ -8,7 +8,7 @@ public static class ByteExtensions
|
||||
/// <summary>
|
||||
/// base64编码
|
||||
/// </summary>
|
||||
/// <param name="me">待编码的字节数组</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>编码后的base64字符串</returns>
|
||||
public static string Base64(this byte[] me)
|
||||
{
|
||||
@ -18,7 +18,7 @@ public static class ByteExtensions
|
||||
/// <summary>
|
||||
/// 将字节数组解码成字符串
|
||||
/// </summary>
|
||||
/// <param name="me">字节数组</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="e">字符串使用的编码方式</param>
|
||||
/// <returns>解码后的原始字符串</returns>
|
||||
public static string HexDe(this byte[] me, Encoding e)
|
||||
@ -29,7 +29,7 @@ public static class ByteExtensions
|
||||
/// <summary>
|
||||
/// 将字节数组解码成字符串
|
||||
/// </summary>
|
||||
/// <param name="me">字节数组</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>解码后的原始字符串</returns>
|
||||
public static string HexDe(this byte[] me)
|
||||
{
|
||||
@ -43,17 +43,17 @@ public static class ByteExtensions
|
||||
/// <param name="upperCase">是否大写</param>
|
||||
/// <param name="splitShar">字节间分隔符</param>
|
||||
/// <param name="splitInterval">分隔符跳跃字节数</param>
|
||||
public static string String(this IEnumerable<byte> me, bool upperCase = true, string splitShar = ""
|
||||
, int splitInterval = 1)
|
||||
public static string Str(this IEnumerable<byte> me, bool upperCase = true, string splitShar = ""
|
||||
, int splitInterval = 1)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var i = 0;
|
||||
foreach (var c in me.Select(x => x.ToString(upperCase ? "X2" : "x2", CultureInfo.InvariantCulture))) {
|
||||
if (i++ % splitInterval == 0) {
|
||||
sb.Append(splitShar);
|
||||
_ = sb.Append(splitShar);
|
||||
}
|
||||
|
||||
sb.Append(c);
|
||||
_ = sb.Append(c);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
@ -1,8 +1,7 @@
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
#pragma warning disable SA1300
|
||||
#pragma warning disable IDE1006
|
||||
#pragma warning disable SA1300, IDE1006
|
||||
namespace NSExt.Extensions;
|
||||
|
||||
/// <summary>
|
||||
@ -10,23 +9,10 @@ namespace NSExt.Extensions;
|
||||
/// </summary>
|
||||
public static class DateTimeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 将一个过去时间对象与当前时间相减转换成“xx以前”的字符串, 如2秒以前, 3天以前
|
||||
/// </summary>
|
||||
/// <param name="me">时间对象</param>
|
||||
/// <returns>字符串</returns>
|
||||
public static string TimeAgo(this DateTime me)
|
||||
{
|
||||
var ts = DateTime.Now - me;
|
||||
return ts.Days > 0 ? ts.Days + "天前" :
|
||||
ts.Hours > 0 ? ts.Hours + "小时前" :
|
||||
ts.Minutes > 0 ? ts.Minutes + "分钟前" : ts.Seconds + "秒前";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定时间的世界协调时的unix时间戳形式
|
||||
/// </summary>
|
||||
/// <param name="me">指定时间</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>unix时间戳</returns>
|
||||
public static long TimeUnixUtc(this DateTime me)
|
||||
{
|
||||
@ -41,6 +27,31 @@ public static class DateTimeExtensions
|
||||
return (me.ToUniversalTime().Ticks - 621355968000000000) / 10000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ToString 的 Invariant 版本
|
||||
/// </summary>
|
||||
public static string ToInvString(this DateTime me)
|
||||
{
|
||||
return me.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一个过去时间对象与当前时间相减转换成“xx以前”的字符串, 如2秒以前, 3天以前
|
||||
/// </summary>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>字符串</returns>
|
||||
public static string UtcTimeAgo(this DateTime me)
|
||||
{
|
||||
var ts = DateTime.UtcNow - me;
|
||||
return ts.Days switch {
|
||||
> 0 => ts.Days + "天前"
|
||||
, _ => ts.Hours switch {
|
||||
> 0 => ts.Hours + "小时前"
|
||||
, _ => ts.Minutes switch { > 0 => ts.Minutes + "分钟前", _ => ts.Seconds + "秒前" }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// yyyy_MM
|
||||
/// </summary>
|
@ -1,7 +1,10 @@
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
|
||||
namespace NSExt.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// DbCommandExtensions
|
||||
/// DbCommandExtensions
|
||||
/// </summary>
|
||||
public static class DbCommandExtensions
|
||||
{
|
||||
@ -10,22 +13,23 @@ public static class DbCommandExtensions
|
||||
/// </summary>
|
||||
public static string ParameterFormat(this DbCommand me)
|
||||
{
|
||||
//var aa = pars.ToDictionary(it => it.ParameterName, it => it.Value);
|
||||
var sql = me.CommandText;
|
||||
|
||||
//应逆向替换,否则由于 多个表的过滤器问题导致替换不完整 如 @TenantId1 @TenantId10
|
||||
// 应逆向替换,否则由于 多个表的过滤器问题导致替换不完整 如 @TenantId1 @TenantId10
|
||||
for (var i = me.Parameters.Count - 1; i >= 0; i--) {
|
||||
#pragma warning disable IDE0072
|
||||
sql = me.Parameters[i].DbType switch {
|
||||
#pragma warning restore IDE0072
|
||||
DbType.String or DbType.DateTime or DbType.Date or DbType.Time or DbType.DateTime2
|
||||
or DbType.DateTimeOffset or DbType.Guid or DbType.VarNumeric or DbType.AnsiStringFixedLength
|
||||
or DbType.AnsiString
|
||||
or DbType.StringFixedLength =>
|
||||
sql.Replace(me.Parameters[i].ParameterName, "'" + me.Parameters[i].Value + "'")
|
||||
, DbType.Boolean => sql.Replace(//
|
||||
me.Parameters[i].ParameterName
|
||||
, Convert.ToBoolean(me.Parameters[i].Value, CultureInfo.InvariantCulture) ? "1" : "0")
|
||||
or DbType.AnsiString or DbType.StringFixedLength => sql.Replace( //
|
||||
me.Parameters[i].ParameterName, "'" + me.Parameters[i].Value + "'")
|
||||
, DbType.Boolean => sql.Replace( //
|
||||
me.Parameters[i].ParameterName
|
||||
, me.Parameters[i].Value != DBNull.Value &&
|
||||
Convert.ToBoolean(me.Parameters[i].Value, CultureInfo.InvariantCulture)
|
||||
? "1"
|
||||
: "0")
|
||||
, _ => sql.Replace(me.Parameters[i].ParameterName, me.Parameters[i].Value?.ToString())
|
||||
};
|
||||
}
|
@ -8,12 +8,19 @@ public static class DecimalExtensions
|
||||
/// <summary>
|
||||
/// 四舍五入后的近似值
|
||||
/// </summary>
|
||||
/// <param name="me">数字</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="place">小数点位数</param>
|
||||
/// <returns>处理后的值</returns>
|
||||
public static decimal Round(this decimal me, int place)
|
||||
{
|
||||
var dec = Math.Round(me, place);
|
||||
return dec;
|
||||
return Math.Round(me, place);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ToString 的 Invariant 版本
|
||||
/// </summary>
|
||||
public static string ToInvString(this decimal me)
|
||||
{
|
||||
return me.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
41
src/backend/NSExt/Extensions/EnumExtensions.cs
Normal file
41
src/backend/NSExt/Extensions/EnumExtensions.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Reflection;
|
||||
using NSExt.Attributes;
|
||||
|
||||
namespace NSExt.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// EnumExtensions
|
||||
/// </summary>
|
||||
public static class EnumExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取显示特性
|
||||
/// </summary>
|
||||
public static DisplayAttribute GetDisplay(this Enum me)
|
||||
{
|
||||
return me.GetAttributeOfType<DisplayAttribute>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取枚举的本地化资源描述
|
||||
/// </summary>
|
||||
public static string ResDesc<T>(this Enum e)
|
||||
{
|
||||
var typeOfEnum = e.GetType();
|
||||
var typeOfField = typeOfEnum.GetField(Enum.GetName(typeOfEnum, e)!);
|
||||
var resDescAttr = typeOfField!.GetCustomAttribute<ResourceDescriptionAttribute<T>>(true);
|
||||
return resDescAttr is null
|
||||
? Enum.GetName(typeOfEnum, e)
|
||||
: typeof(T).GetProperty(resDescAttr.ResourceName)?.GetValue(null) as string;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过类泛型类型获取特性
|
||||
/// </summary>
|
||||
private static T GetAttributeOfType<T>(this Enum me)
|
||||
where T : Attribute
|
||||
{
|
||||
return me.GetType().GetMember(me.ToString())[0].GetCustomAttributes<T>(false).FirstOrDefault();
|
||||
}
|
||||
}
|
46
src/backend/NSExt/Extensions/EnumerableExtensions.cs
Normal file
46
src/backend/NSExt/Extensions/EnumerableExtensions.cs
Normal file
@ -0,0 +1,46 @@
|
||||
namespace NSExt.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// EnumerableExtensions
|
||||
/// </summary>
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 异步累加器函数
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">InvalidOperationException</exception>
|
||||
public static async Task<TSource> AggregateAsync<TSource>( //
|
||||
this IEnumerable<TSource> source, Func<TSource, TSource, Task<TSource>> func)
|
||||
{
|
||||
using var e = source.GetEnumerator();
|
||||
if (!e.MoveNext()) {
|
||||
throw new InvalidOperationException("Sequence contains no elements");
|
||||
}
|
||||
|
||||
var result = e.Current;
|
||||
while (e.MoveNext()) {
|
||||
result = await func(result, e.Current).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将列表转成分隔符分隔的字符串
|
||||
/// </summary>
|
||||
public static string Join(this IEnumerable<object> me, string separator)
|
||||
{
|
||||
return string.Join(separator, me);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断对象是否为null或不存在子元素(如果为集合对象)
|
||||
/// </summary>
|
||||
/// <typeparam name="T">对象类型</typeparam>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>空则返回true</returns>
|
||||
public static bool NullOrEmpty<T>(this IEnumerable<T> me)
|
||||
{
|
||||
return me?.Any() != true;
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ public static class GenericExtensions
|
||||
/// 从指定的对象拷贝属性
|
||||
/// </summary>
|
||||
/// <typeparam name="T">对象类型</typeparam>
|
||||
/// <param name="me">拷贝目标</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="copyObj">拷贝来源</param>
|
||||
/// <param name="propNameList">需要处理的属性名</param>
|
||||
/// <param name="isIncludeOrExclude">True包含,false排除</param>
|
@ -17,17 +17,27 @@ public static class IntExtensions
|
||||
/// <summary>
|
||||
/// 生成随机数
|
||||
/// </summary>
|
||||
/// <param name="me">大于等于[0],小于[1]</param>
|
||||
/// <param name="me">me</param>
|
||||
public static int Rand(this int[] me)
|
||||
{
|
||||
return new Random(Guid.NewGuid().GetHashCode()).Next(me[0], me[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ToString 的 Invariant 版本
|
||||
/// </summary>
|
||||
public static string ToInvString(this int me)
|
||||
{
|
||||
return me.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换成ipv4
|
||||
/// </summary>
|
||||
public static string ToIpV4(this int me)
|
||||
{
|
||||
return string.Join(".", BitConverter.GetBytes(me).Reverse());
|
||||
var bytes = BitConverter.GetBytes(me);
|
||||
Array.Reverse(bytes);
|
||||
return string.Join('.', bytes);
|
||||
}
|
||||
}
|
86
src/backend/NSExt/Extensions/LoggerExtensions.cs
Normal file
86
src/backend/NSExt/Extensions/LoggerExtensions.cs
Normal file
@ -0,0 +1,86 @@
|
||||
// ReSharper disable TemplateIsNotCompileTimeConstantProblem
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace NSExt.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// LoggerExtensions
|
||||
/// </summary>
|
||||
public static class LoggerExtensions
|
||||
{
|
||||
private const string _MESSAGE_S_THREAD_ID_CALLER_NAME_CALLER_FILE_PATH_CALLER_LINE_NUMBER
|
||||
= "{Message} <s:{CallerName}@{CallerFilePath}:{CallerLineNumber}>";
|
||||
|
||||
private static readonly Action<ILogger, string, string, string, string, Exception> _logDebug
|
||||
= LoggerMessage.Define<string, string, string, string>(LogLevel.Debug, default
|
||||
, _MESSAGE_S_THREAD_ID_CALLER_NAME_CALLER_FILE_PATH_CALLER_LINE_NUMBER);
|
||||
|
||||
private static readonly Action<ILogger, string, string, string, string, Exception> _logError
|
||||
= LoggerMessage.Define<string, string, string, string>(LogLevel.Error, default
|
||||
, _MESSAGE_S_THREAD_ID_CALLER_NAME_CALLER_FILE_PATH_CALLER_LINE_NUMBER);
|
||||
|
||||
private static readonly Action<ILogger, string, string, string, string, Exception> _logFatal
|
||||
= LoggerMessage.Define<string, string, string, string>(LogLevel.Critical, default
|
||||
, _MESSAGE_S_THREAD_ID_CALLER_NAME_CALLER_FILE_PATH_CALLER_LINE_NUMBER);
|
||||
|
||||
private static readonly Action<ILogger, string, string, string, string, Exception> _logInfo
|
||||
= LoggerMessage.Define<string, string, string, string>(LogLevel.Information, default
|
||||
, _MESSAGE_S_THREAD_ID_CALLER_NAME_CALLER_FILE_PATH_CALLER_LINE_NUMBER);
|
||||
|
||||
private static readonly Action<ILogger, string, string, string, string, Exception> _logWarn
|
||||
= LoggerMessage.Define<string, string, string, string>(LogLevel.Warning, default
|
||||
, _MESSAGE_S_THREAD_ID_CALLER_NAME_CALLER_FILE_PATH_CALLER_LINE_NUMBER);
|
||||
|
||||
/// <summary>
|
||||
/// Debug
|
||||
/// </summary>
|
||||
public static void Debug(this ILogger me, object message, [CallerMemberName] string callerName = null
|
||||
, [CallerFilePath] string callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
_logDebug(me, message.ToString(), callerName, Path.GetFileName(callerFilePath)
|
||||
, callerLineNumber.ToString(CultureInfo.InvariantCulture), null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error
|
||||
/// </summary>
|
||||
public static void Error(this ILogger me, object message, [CallerMemberName] string callerName = null
|
||||
, [CallerFilePath] string callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
_logError(me, message.ToString(), callerName, Path.GetFileName(callerFilePath)
|
||||
, callerLineNumber.ToString(CultureInfo.InvariantCulture), null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fatal
|
||||
/// </summary>
|
||||
public static void Fatal(this ILogger me, object message, Exception ex = null
|
||||
, [CallerMemberName] string callerName = null, [CallerFilePath] string callerFilePath = null
|
||||
, [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
_logFatal(me, message.ToString(), callerName, Path.GetFileName(callerFilePath)
|
||||
, callerLineNumber.ToString(CultureInfo.InvariantCulture), ex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Info
|
||||
/// </summary>
|
||||
public static void Info(this ILogger me, object message, [CallerMemberName] string callerName = null
|
||||
, [CallerFilePath] string callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
_logInfo(me, message.ToString(), callerName, Path.GetFileName(callerFilePath)
|
||||
, callerLineNumber.ToString(CultureInfo.InvariantCulture), null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Warn
|
||||
/// </summary>
|
||||
public static void Warn(this ILogger me, object message, [CallerMemberName] string callerName = null
|
||||
, [CallerFilePath] string callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
_logWarn(me, message.ToString(), callerName, Path.GetFileName(callerFilePath)
|
||||
, callerLineNumber.ToString(CultureInfo.InvariantCulture), null);
|
||||
}
|
||||
}
|
42
src/backend/NSExt/Extensions/LongExtensions.cs
Normal file
42
src/backend/NSExt/Extensions/LongExtensions.cs
Normal file
@ -0,0 +1,42 @@
|
||||
namespace NSExt.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// LongExtensions
|
||||
/// </summary>
|
||||
public static class LongExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 判断枚举是否包含某个位
|
||||
/// </summary>
|
||||
public static bool HasFlag<T>(this long me, T flag)
|
||||
where T : Enum
|
||||
{
|
||||
var val = (long)(object)flag;
|
||||
return (me & val) == val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成随机数
|
||||
/// </summary>
|
||||
/// <param name="me">me</param>
|
||||
public static long Rand(this long[] me)
|
||||
{
|
||||
return new Random(Guid.NewGuid().GetHashCode()).NextInt64(me[0], me[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 1970毫秒数转换成日期对象
|
||||
/// </summary>
|
||||
public static DateTime Time(this long msFrom1970)
|
||||
{
|
||||
return DateTime.UnixEpoch.AddMilliseconds(msFrom1970).ToLocalTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ToString 的 Invariant 版本
|
||||
/// </summary>
|
||||
public static string ToInvString(this long me)
|
||||
{
|
||||
return me.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
30
src/backend/NSExt/Extensions/ObjectExtensions.cs
Normal file
30
src/backend/NSExt/Extensions/ObjectExtensions.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace NSExt.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// ObjectExtensions
|
||||
/// </summary>
|
||||
public static class ObjectExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 将一个对象序列化成json文本
|
||||
/// </summary>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>json文本</returns>
|
||||
public static string Json(this object me)
|
||||
{
|
||||
return JsonSerializer.Serialize(me);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一个对象序列化成json文本
|
||||
/// </summary>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="options">序列化选项</param>
|
||||
/// <returns>json文本</returns>
|
||||
public static string Json(this object me, JsonSerializerOptions options)
|
||||
{
|
||||
return JsonSerializer.Serialize(me, options);
|
||||
}
|
||||
}
|
@ -25,6 +25,8 @@ public static class StreamExtensions
|
||||
/// </summary>
|
||||
public static bool IsTextStream(this Stream me)
|
||||
{
|
||||
return me.FirstByteIndex(new byte[] { 0x00, 0xff }) < 0;
|
||||
#pragma warning disable IDE0300
|
||||
return me.FirstByteIndex([0x00, 0xff]) < 0;
|
||||
#pragma warning restore IDE0300
|
||||
}
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
|
||||
#pragma warning disable CA1720
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using System.Web;
|
||||
using Microsoft.CodeAnalysis.CSharp.Scripting;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
using NSExt.Constant;
|
||||
|
||||
namespace NSExt.Extensions;
|
||||
@ -11,49 +12,107 @@ namespace NSExt.Extensions;
|
||||
/// <summary>
|
||||
/// StringExtensions
|
||||
/// </summary>
|
||||
public static class StringExtensions
|
||||
public static partial class StringExtensions
|
||||
{
|
||||
private static readonly JsonSerializerOptions _defaultJsonSerializerOptions
|
||||
= default(JsonSerializerOptions).NewJsonSerializerOptions();
|
||||
private const string _CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
private static readonly Regex _regexIpV4 = RegexIpV4();
|
||||
|
||||
/// <summary>
|
||||
/// aes加密
|
||||
/// </summary>
|
||||
/// <param name="me">要加密的串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="key">密钥</param>
|
||||
public static string Aes(this string me, string key)
|
||||
{
|
||||
using var aes = System.Security.Cryptography.Aes.Create();
|
||||
aes.Padding = PaddingMode.PKCS7;
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Key = key.Hex();
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Key = key.Hex();
|
||||
using var encryptor = aes.CreateEncryptor();
|
||||
var bytes = me.Hex();
|
||||
var decrypted = encryptor.TransformFinalBlock(bytes, 0, bytes.Length);
|
||||
var bytes = me.Hex();
|
||||
var decrypted = encryptor.TransformFinalBlock(bytes, 0, bytes.Length);
|
||||
return decrypted.Base64();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// aes解密
|
||||
/// </summary>
|
||||
/// <param name="me">要加密的串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="key">密钥</param>
|
||||
public static string AesDe(this string me, string key)
|
||||
{
|
||||
using var aes = System.Security.Cryptography.Aes.Create();
|
||||
aes.Padding = PaddingMode.PKCS7;
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Key = key.Hex();
|
||||
aes.Mode = CipherMode.ECB;
|
||||
aes.Key = key.Hex();
|
||||
using var encryptor = aes.CreateDecryptor();
|
||||
var bytes = me.Base64De();
|
||||
var decrypted = encryptor.TransformFinalBlock(bytes, 0, bytes.Length);
|
||||
var bytes = me.Base64De();
|
||||
var decrypted = encryptor.TransformFinalBlock(bytes, 0, bytes.Length);
|
||||
return decrypted.HexDe();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定的输入字符串进行Base62解码
|
||||
/// </summary>
|
||||
/// <exception cref="ArgumentException">ArgumentException</exception>
|
||||
public static string Base62Decode(this string me)
|
||||
{
|
||||
BigInteger result = 0;
|
||||
|
||||
foreach (var index in me.Select(c => _CHARACTERS.IndexOf(c)))
|
||||
{
|
||||
if (index < 0)
|
||||
{
|
||||
throw new ArgumentException("Invalid character in Base62 string.");
|
||||
}
|
||||
|
||||
result = result * 62 + index;
|
||||
}
|
||||
|
||||
// Convert BigInteger back to byte array and then to string
|
||||
var bytes = result.ToByteArray();
|
||||
|
||||
// Handle the sign bit
|
||||
if (bytes[^1] == 0)
|
||||
{
|
||||
Array.Resize(ref bytes, bytes.Length - 1);
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定的输入字符串进行Base62编码
|
||||
/// </summary>
|
||||
public static string Base62Encode(this string me)
|
||||
{
|
||||
// Convert string to byte array
|
||||
var bytes = Encoding.UTF8.GetBytes(me);
|
||||
|
||||
// Convert byte array to BigInteger for easier processing
|
||||
var bigInteger = new BigInteger(bytes);
|
||||
|
||||
if (bigInteger == 0)
|
||||
{
|
||||
return _CHARACTERS[0].ToString();
|
||||
}
|
||||
|
||||
var result = new StringBuilder();
|
||||
|
||||
while (bigInteger > 0)
|
||||
{
|
||||
var remainder = (int)(bigInteger % 62);
|
||||
bigInteger /= 62;
|
||||
_ = result.Insert(0, _CHARACTERS[remainder]);
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// base64编码
|
||||
/// </summary>
|
||||
/// <param name="me">待base64编码的字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="e">字符串的编码方式</param>
|
||||
/// <returns>编码后的base64字符串</returns>
|
||||
public static string Base64(this string me, Encoding e)
|
||||
@ -64,7 +123,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// base64解码
|
||||
/// </summary>
|
||||
/// <param name="me">待解码的字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>解码后的原始字节数组</returns>
|
||||
public static byte[] Base64De(this string me)
|
||||
{
|
||||
@ -74,7 +133,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// base64解码
|
||||
/// </summary>
|
||||
/// <param name="me">待解码的字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="e">字符串的编码方式</param>
|
||||
/// <returns>解码后的原始字符串</returns>
|
||||
public static string Base64De(this string me, Encoding e)
|
||||
@ -82,6 +141,22 @@ public static class StringExtensions
|
||||
return e.GetString(me.Base64De());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解码避免转义的Base64
|
||||
/// </summary>
|
||||
public static string Base64InUrlDecode(this string me)
|
||||
{
|
||||
return me.Replace("-", "+").Replace("_", "/");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编码避免转义的Base64
|
||||
/// </summary>
|
||||
public static string Base64InUrlEncode(this string me)
|
||||
{
|
||||
return me.Replace("+", "-").Replace("/", "_");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将易于web传输的base64web字符串转换为原生base64
|
||||
/// </summary>
|
||||
@ -100,10 +175,18 @@ public static class StringExtensions
|
||||
return me.Replace("+", "-").Replace("/", "_").Replace("=", ".");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算Crc32
|
||||
/// </summary>
|
||||
public static int Crc32(this string me)
|
||||
{
|
||||
return BitConverter.ToInt32(System.IO.Hashing.Crc32.Hash(Encoding.UTF8.GetBytes(me)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字符串转换成日期对象
|
||||
/// </summary>
|
||||
/// <param name="me">待转换字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>转换后的日期对象</returns>
|
||||
public static DateTime DateTime(this string me)
|
||||
{
|
||||
@ -113,7 +196,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 将字符串转换成日期对象
|
||||
/// </summary>
|
||||
/// <param name="me">待转换字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="format">日期格式</param>
|
||||
/// <returns>转换后的日期对象</returns>
|
||||
public static DateTime DateTimeExact(this string me, string format)
|
||||
@ -124,32 +207,30 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 将字符串转换成日期对象
|
||||
/// </summary>
|
||||
/// <param name="me">待转换字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="format">日期格式</param>
|
||||
/// <param name="def">转换失败时返回的日期对象</param>
|
||||
/// <returns>转换后的日期对象</returns>
|
||||
public static DateTime DateTimeExactTry(this string me, string format, DateTime def)
|
||||
{
|
||||
return !System.DateTime.TryParseExact(me, format, CultureInfo.CurrentCulture, DateTimeStyles.None, out var ret)
|
||||
? def
|
||||
: ret;
|
||||
return !System.DateTime.TryParseExact(me, format, CultureInfo.CurrentCulture, DateTimeStyles.None, out var ret) ? def : ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字符串转换成日期对象
|
||||
/// </summary>
|
||||
/// <param name="me">待转换字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="def">转换失败时返回的日期对象</param>
|
||||
/// <returns>转换后的日期对象</returns>
|
||||
public static DateTime DateTimeTry(this string me, DateTime def)
|
||||
{
|
||||
return !System.DateTime.TryParse(me, out var ret) ? def : ret;
|
||||
return !System.DateTime.TryParse(me, CultureInfo.InvariantCulture, DateTimeStyles.None, out var ret) ? def : ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// string to decimal
|
||||
/// </summary>
|
||||
/// <param name="me">string</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>decimal</returns>
|
||||
public static decimal Dec(this string me)
|
||||
{
|
||||
@ -159,7 +240,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 尝试将字符串转为decimal
|
||||
/// </summary>
|
||||
/// <param name="me">字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="def">转换失败后返回的默认值</param>
|
||||
/// <returns>转换后的decimal</returns>
|
||||
public static decimal DecTry(this string me, decimal def)
|
||||
@ -170,7 +251,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// string to double
|
||||
/// </summary>
|
||||
/// <param name="me">string</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>Int32</returns>
|
||||
public static double Double(this string me)
|
||||
{
|
||||
@ -195,10 +276,19 @@ public static class StringExtensions
|
||||
return !System.Enum.TryParse(typeof(T), name, out var ret) ? def : (T)ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行C#代码
|
||||
/// </summary>
|
||||
public static Task<T> ExecuteCSharpCodeAsync<T>(this string me, Assembly[] assemblies, params string[] importNamespaces)
|
||||
{
|
||||
// 使用 Roslyn 编译并执行代码
|
||||
return CSharpScript.EvaluateAsync<T>(me, ScriptOptions.Default.WithReferences(assemblies).WithImports(importNamespaces));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// string to float
|
||||
/// </summary>
|
||||
/// <param name="me">string</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>Int32</returns>
|
||||
public static float Float(this string me)
|
||||
{
|
||||
@ -208,7 +298,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 将字符串转为guid
|
||||
/// </summary>
|
||||
/// <param name="me">字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
public static Guid Guid(this string me)
|
||||
{
|
||||
return System.Guid.Parse(me);
|
||||
@ -217,7 +307,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 将字符串转换成guid
|
||||
/// </summary>
|
||||
/// <param name="me">字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="def">转换失败的返回值</param>
|
||||
public static Guid Guid(this string me, Guid def)
|
||||
{
|
||||
@ -227,7 +317,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 将字符串转换成字节数组形式
|
||||
/// </summary>
|
||||
/// <param name="me">字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="e">字符串使用的编码</param>
|
||||
/// <returns>字节数组</returns>
|
||||
public static byte[] Hex(this string me, Encoding e)
|
||||
@ -238,7 +328,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 将字符串转换成字节数组形式
|
||||
/// </summary>
|
||||
/// <param name="me">字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>字节数组</returns>
|
||||
public static byte[] Hex(this string me)
|
||||
{
|
||||
@ -256,9 +346,11 @@ public static class StringExtensions
|
||||
{
|
||||
using var hmacSha1 = new HMACSHA1(e.GetBytes(secret));
|
||||
|
||||
return BitConverter.ToString(hmacSha1.ComputeHash(e.GetBytes(me)))
|
||||
.Replace("-", string.Empty)
|
||||
.ToLower(CultureInfo.CurrentCulture);
|
||||
#if NET9_0_OR_GREATER
|
||||
return Convert.ToHexStringLower(hmacSha1.ComputeHash(e.GetBytes(me)));
|
||||
#else
|
||||
return BitConverter.ToString(hmacSha1.ComputeHash(e.GetBytes(me))).Replace("-", string.Empty).ToLower(CultureInfo.CurrentCulture);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -272,7 +364,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 解码html编码
|
||||
/// </summary>
|
||||
/// <param name="me">html编码后的字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>解码后的原始字符串</returns>
|
||||
public static string HtmlDe(this string me)
|
||||
{
|
||||
@ -282,7 +374,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// string to Int32
|
||||
/// </summary>
|
||||
/// <param name="me">string</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>Int32</returns>
|
||||
public static int Int32(this string me)
|
||||
{
|
||||
@ -292,7 +384,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 尝试将字符串转为int32
|
||||
/// </summary>
|
||||
/// <param name="me">字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="def">转换失败后返回的默认值</param>
|
||||
/// <returns>转换后的int32</returns>
|
||||
public static int Int32Try(this string me, int def)
|
||||
@ -303,7 +395,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// string to Int64
|
||||
/// </summary>
|
||||
/// <param name="me">string</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>Int64</returns>
|
||||
public static long Int64(this string me)
|
||||
{
|
||||
@ -313,7 +405,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 尝试将字符串转为int64
|
||||
/// </summary>
|
||||
/// <param name="me">字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="def">转换失败后返回的默认值</param>
|
||||
/// <returns>转换后的int64</returns>
|
||||
public static long Int64Try(this string me, long def)
|
||||
@ -332,22 +424,25 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 是否base64字符串
|
||||
/// </summary>
|
||||
/// <param name="me">字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
public static bool IsBase64String(this string me)
|
||||
{
|
||||
// 一个合法的Base64,有着以下特征:
|
||||
// 字符串的长度为4的整数倍。
|
||||
// 字符串的符号取值只能在A -Z, a -z, 0 -9, +, /, =共计65个字符中,且 = 如果出现就必须在结尾出现。
|
||||
if (!me.All(x => x.IsBase64Character())) {
|
||||
if (!me.All(x => x.IsBase64Character()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (me.Length % 4 != 0) {
|
||||
if (me.Length % 4 != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var firstEqualSignPos = me.IndexOf('=');
|
||||
if (firstEqualSignPos < 0) {
|
||||
if (firstEqualSignPos < 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -355,6 +450,37 @@ public static class StringExtensions
|
||||
return lastEqualSignPos == me.Length - 1 && me[firstEqualSignPos..lastEqualSignPos].All(x => x == '=');
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否IPV4地址
|
||||
/// </summary>
|
||||
public static bool IsIpV4(this string me)
|
||||
{
|
||||
return _regexIpV4.IsMatch(me);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否json字符串
|
||||
/// </summary>
|
||||
/// <param name="me">me</param>
|
||||
public static bool IsJsonString(this string me)
|
||||
{
|
||||
if (me.NullOrEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_ = JsonDocument.Parse(me);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 中文姓名打马赛克
|
||||
/// </summary>
|
||||
@ -366,7 +492,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 对一个手机号进行掩码处理
|
||||
/// </summary>
|
||||
/// <param name="me">手机号</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>掩码后的手机号</returns>
|
||||
public static string MaskMobile(this string me)
|
||||
{
|
||||
@ -376,20 +502,24 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 对一个字符串进行md5hash运算
|
||||
/// </summary>
|
||||
/// <param name="me">字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="e">字符串使用的编码</param>
|
||||
/// <returns>hash摘要的16进制文本形式(无连字符小写)</returns>
|
||||
public static string Md5(this string me, Encoding e)
|
||||
{
|
||||
#if NET9_0_OR_GREATER
|
||||
return Convert.ToHexStringLower(MD5.HashData(e.GetBytes(me)));
|
||||
#else
|
||||
return BitConverter.ToString(MD5.HashData(e.GetBytes(me)))
|
||||
.Replace("-", string.Empty)
|
||||
.ToLower(CultureInfo.CurrentCulture);
|
||||
.Replace("-", string.Empty)
|
||||
.ToLower(CultureInfo.CurrentCulture);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断字符串是否为null或不存在子元素(如果为集合对象);如果为空,返回指定的默认值,否则返回字符串本身
|
||||
/// </summary>
|
||||
/// <param name="me">指定字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="defVal">指定的默认值</param>
|
||||
/// <returns>如果为空,返回指定的默认值,否则返回字符串本身</returns>
|
||||
public static string NullOrEmpty(this string me, string defVal)
|
||||
@ -408,30 +538,30 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 反序列化一个文件获得指定类型的数据对象
|
||||
/// </summary>
|
||||
/// <param name="me">等待反序列化的json文本</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="options">序列化选项</param>
|
||||
/// <returns>反序列化后生成的对象</returns>
|
||||
public static T Object<T>(this string me, JsonSerializerOptions options = null)
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(me, options ?? _defaultJsonSerializerOptions);
|
||||
return JsonSerializer.Deserialize<T>(me, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化一个文件获得指定类型的数据对象
|
||||
/// </summary>
|
||||
/// <param name="me">等待反序列化的json文本</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="type">实际类型</param>
|
||||
/// <param name="options">序列化选项</param>
|
||||
/// <returns>反序列化后生成的对象</returns>
|
||||
public static object Object(this string me, Type type, JsonSerializerOptions options = null)
|
||||
{
|
||||
return JsonSerializer.Deserialize(me, type, options ?? _defaultJsonSerializerOptions);
|
||||
return JsonSerializer.Deserialize(me, type, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成密码
|
||||
/// </summary>
|
||||
/// <param name="me">密码原文</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>密文</returns>
|
||||
public static string Pwd(this string me)
|
||||
{
|
||||
@ -441,7 +571,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 移除字符串中的html标签
|
||||
/// </summary>
|
||||
/// <param name="me">字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>处理之后的字符串</returns>
|
||||
public static string RemoveHtmlTag(this string me)
|
||||
{
|
||||
@ -459,20 +589,24 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 对一个字符串进行sha1 hash运算
|
||||
/// </summary>
|
||||
/// <param name="me">字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="e">字符串使用的编码</param>
|
||||
/// <returns>hash摘要的16进制文本形式(无连字符小写)</returns>
|
||||
public static string Sha1(this string me, Encoding e)
|
||||
{
|
||||
#if NET9_0_OR_GREATER
|
||||
return Convert.ToHexStringLower(SHA1.HashData(e.GetBytes(me)));
|
||||
#else
|
||||
return BitConverter.ToString(SHA1.HashData(e.GetBytes(me)))
|
||||
.Replace("-", string.Empty)
|
||||
.ToLower(CultureInfo.CurrentCulture);
|
||||
.Replace("-", string.Empty)
|
||||
.ToLower(CultureInfo.CurrentCulture);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 蛇形命名
|
||||
/// </summary>
|
||||
public static string Snakecase(this string me)
|
||||
public static string SnakeCase(this string me)
|
||||
{
|
||||
return Regexes.RegexUpLetter.Replace(me, "-$1").ToLower(CultureInfo.InvariantCulture).TrimStart('-');
|
||||
}
|
||||
@ -482,7 +616,8 @@ public static class StringExtensions
|
||||
/// </summary>
|
||||
public static string Sub(this string me, int startIndex, int length)
|
||||
{
|
||||
if (startIndex + length > me.Length) {
|
||||
if (startIndex + length > me.Length)
|
||||
{
|
||||
length = me.Length - startIndex;
|
||||
}
|
||||
|
||||
@ -497,6 +632,30 @@ public static class StringExtensions
|
||||
return $"<pre>{me}</pre>";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 首字母小写
|
||||
/// </summary>
|
||||
public static string ToLowerCamelCase(this string me)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(me) ? me : string.Concat(me[0].ToString(CultureInfo.InvariantCulture).ToLowerInvariant(), me.AsSpan(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 首字母大写
|
||||
/// </summary>
|
||||
public static string ToUpperCamelCase(this string me)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(me) ? me : string.Concat(me[0].ToString().ToUpperInvariant(), me.AsSpan(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 去掉前部字符串
|
||||
/// </summary>
|
||||
public static string TrimPrefix(this string me, string clearStr)
|
||||
{
|
||||
return Regex.Replace(me, $"^{clearStr}", string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将连续多个空格替换成一个空格
|
||||
/// </summary>
|
||||
@ -508,20 +667,31 @@ public static class StringExtensions
|
||||
return ret == me ? ret : ret.TrimSpaces();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 去掉尾部字符串
|
||||
/// </summary>
|
||||
public static string TrimSuffix(this string me, string clearStr)
|
||||
{
|
||||
return Regex.Replace(me, $"{clearStr}$", string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将\ux0000 、 %u0000 、 &#x0000; 编码转换成可读字符串
|
||||
/// </summary>
|
||||
public static string UnicodeDe(this string me)
|
||||
{
|
||||
#pragma warning disable S3358, RCS1238
|
||||
const string replacement = "&#x$1;";
|
||||
return me.Contains(@"\u") ? Regexes.RegexBacksLantUnicode.Replace(me, replacement).HtmlDe() :
|
||||
me.Contains(@"%u") ? Regexes.RegexPercentUnicode.Replace(me, replacement).HtmlDe() : me.HtmlDe();
|
||||
return !me.Contains(@"\u")
|
||||
? me.Contains("%u") ? Regexes.RegexPercentUnicode.Replace(me, replacement).HtmlDe() : me.HtmlDe()
|
||||
: Regexes.RegexBacksLantUnicode.Replace(me, replacement).HtmlDe();
|
||||
#pragma warning restore S3358, RCS1238
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// url编码
|
||||
/// </summary>
|
||||
/// <param name="me">字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>url编码后的字符串</returns>
|
||||
public static string Url(this string me)
|
||||
{
|
||||
@ -531,7 +701,7 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// 解码url编码
|
||||
/// </summary>
|
||||
/// <param name="me">url编码后的字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <returns>解码后的原始字符串</returns>
|
||||
public static string UrlDe(this string me)
|
||||
{
|
||||
@ -541,15 +711,21 @@ public static class StringExtensions
|
||||
/// <summary>
|
||||
/// MD5 hmac编码
|
||||
/// </summary>
|
||||
/// <param name="me">字符串</param>
|
||||
/// <param name="me">me</param>
|
||||
/// <param name="key">密钥</param>
|
||||
/// <param name="e">字符串使用的编码</param>
|
||||
/// <returns>hash摘要的16进制文本形式(无连字符小写)</returns>
|
||||
private static string Md5Hmac(this string me, string key, Encoding e)
|
||||
{
|
||||
using var md5Hmac = new HMACMD5(e.GetBytes(key));
|
||||
return BitConverter.ToString(md5Hmac.ComputeHash(e.GetBytes(me)))
|
||||
.Replace("-", string.Empty)
|
||||
.ToLower(CultureInfo.CurrentCulture);
|
||||
|
||||
#if NET9_0_OR_GREATER
|
||||
return Convert.ToHexStringLower(md5Hmac.ComputeHash(e.GetBytes(me)));
|
||||
#else
|
||||
return BitConverter.ToString(md5Hmac.ComputeHash(e.GetBytes(me))).Replace("-", string.Empty).ToLower(CultureInfo.CurrentCulture);
|
||||
#endif
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"^(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})){3}$")]
|
||||
private static partial Regex RegexIpV4();
|
||||
}
|
19
src/backend/NSExt/NSExt.csproj
Normal file
19
src/backend/NSExt/NSExt.csproj
Normal file
@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<PackageProjectUrl>https://github.com/nsnail/NSExt.git</PackageProjectUrl>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)/build/code.quality.props"/>
|
||||
<Import Project="$(SolutionDir)/build/nuget.package.props"/>
|
||||
<Import Project="$(SolutionDir)/build/copy.pkg.xml.comment.files.targets"/>
|
||||
<Import Project="$(SolutionDir)/build/prebuild.targets"/>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.13.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.3"/>
|
||||
<PackageReference Include="System.IO.Hashing" Version="9.0.3"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="*.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
|
||||
"settings": {
|
||||
"indentation": {
|
||||
"useTabs": false,
|
||||
"indentationSize": 4
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user