From 60963a3dc175fa89d23a018c88c2980127d3fadb Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Mon, 30 Mar 2020 18:29:33 +0300 Subject: [PATCH 01/11] add connection string param to CLI --- .../Volo/Abp/Cli/Commands/NewCommand.cs | 17 +++++++- .../Steps/ConnectionStringChangeStep.cs | 41 +++++++++++++++++++ .../TemplateProjectBuildPipelineBuilder.cs | 5 +++ .../Cli/ProjectBuilding/ProjectBuildArgs.cs | 7 +++- 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs index c7d7b4a87b..86345da19c 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs @@ -73,6 +73,14 @@ namespace Volo.Abp.Cli.Commands Logger.LogInformation("UI Framework: " + uiFramework); } + var connectionString = commandLineArgs + .Options + .GetOrNull(Options.ConnectionString.Short, Options.ConnectionString.Long); + if (connectionString != null) + { + Logger.LogInformation("Connection string: " + connectionString); + } + var mobileApp = GetMobilePreference(commandLineArgs); if (mobileApp != MobileApp.None) { @@ -122,7 +130,8 @@ namespace Volo.Abp.Cli.Commands mobileApp, gitHubLocalRepositoryPath, templateSource, - commandLineArgs.Options + commandLineArgs.Options, + connectionString ) ); @@ -309,6 +318,12 @@ namespace Volo.Abp.Cli.Commands public const string Long = "template-source"; } + public static class ConnectionString + { + public const string Short = "cs"; + public const string Long = "connection-string"; + } + public static class CreateSolutionFolder { public const string Short = "csf"; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs new file mode 100644 index 0000000000..ba42500007 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Volo.Abp.Cli.ProjectBuilding.Files; + +namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps +{ + public class ConnectionStringChangeStep : ProjectBuildPipelineStep + { + public override void Execute(ProjectBuildContext context) + { + var newConnectionString = context.BuildArgs.ConnectionString; + + var appSettingsJsonFiles = context.Files.Where(f => + f.Name.EndsWith("appsettings.json", StringComparison.OrdinalIgnoreCase)); + + foreach (var appSettingsJson in appSettingsJsonFiles) + { + var appSettingsObject = JsonConvert.DeserializeObject(appSettingsJson.Content); + var oldConnectionString = appSettingsObject.ConnectionStrings.Default; + appSettingsJson.ReplaceText(oldConnectionString, newConnectionString); + } + } + + public class ConnectionStringModel + { + public string Default { get; set; } + } + + public class AppSettingsConnectionStringModel + { + public ConnectionStringModel ConnectionStrings { get; set; } + + public string GetDefaultConnectionString() + { + return ConnectionStrings?.Default; + } + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/TemplateProjectBuildPipelineBuilder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/TemplateProjectBuildPipelineBuilder.cs index a68de8ef62..607ec4f1c7 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/TemplateProjectBuildPipelineBuilder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/TemplateProjectBuildPipelineBuilder.cs @@ -28,6 +28,11 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building pipeline.Steps.Add(new RemoveRootFolderStep()); } + if (context.BuildArgs.ConnectionString != null) + { + pipeline.Steps.Add(new ConnectionStringChangeStep()); + } + pipeline.Steps.Add(new CreateProjectResultZipStep()); return pipeline; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs index ea207bc426..70cb98dff0 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs @@ -27,6 +27,9 @@ namespace Volo.Abp.Cli.ProjectBuilding [CanBeNull] public string TemplateSource { get; set; } + [CanBeNull] + public string ConnectionString { get; set; } + [NotNull] public Dictionary ExtraProperties { get; set; } @@ -39,7 +42,8 @@ namespace Volo.Abp.Cli.ProjectBuilding MobileApp? mobileApp = null, [CanBeNull] string abpGitHubLocalRepositoryPath = null, [CanBeNull] string templateSource = null, - Dictionary extraProperties = null) + Dictionary extraProperties = null, + [CanBeNull] string connectionString = null) { SolutionName = Check.NotNull(solutionName, nameof(solutionName)); TemplateName = templateName; @@ -50,6 +54,7 @@ namespace Volo.Abp.Cli.ProjectBuilding AbpGitHubLocalRepositoryPath = abpGitHubLocalRepositoryPath; TemplateSource = templateSource; ExtraProperties = extraProperties ?? new Dictionary(); + ConnectionString = connectionString; } } } \ No newline at end of file From 25c50b295c3f0dd8cb7c18bc2623e17dac7da008 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Mon, 30 Mar 2020 18:38:20 +0300 Subject: [PATCH 02/11] Update NewCommand.cs --- .../Volo/Abp/Cli/Commands/NewCommand.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs index 86345da19c..222bfee307 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs @@ -73,9 +73,7 @@ namespace Volo.Abp.Cli.Commands Logger.LogInformation("UI Framework: " + uiFramework); } - var connectionString = commandLineArgs - .Options - .GetOrNull(Options.ConnectionString.Short, Options.ConnectionString.Long); + var connectionString = GetConnectionString(commandLineArgs); if (connectionString != null) { Logger.LogInformation("Connection string: " + connectionString); @@ -177,6 +175,12 @@ namespace Volo.Abp.Cli.Commands Logger.LogInformation($"'{projectName}' has been successfully created to '{outputFolder}'"); } + private static string GetConnectionString(CommandLineArgs commandLineArgs) + { + var connectionString = commandLineArgs.Options.GetOrNull(Options.ConnectionString.Short, Options.ConnectionString.Long); + return string.IsNullOrWhiteSpace(connectionString) ? null : connectionString; + } + public string GetUsageInfo() { var sb = new StringBuilder(); From 733dbd44606e892689a0adad4e3c48e034b353df Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Mon, 30 Mar 2020 19:21:21 +0300 Subject: [PATCH 03/11] Update ConnectionStringChangeStep.cs --- .../Steps/ConnectionStringChangeStep.cs | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs index ba42500007..d12bc45866 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Volo.Abp.Cli.ProjectBuilding.Files; namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps @@ -10,31 +12,24 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps { public override void Execute(ProjectBuildContext context) { - var newConnectionString = context.BuildArgs.ConnectionString; + var newConnectionString = "\"Default\": \"" + context.BuildArgs.ConnectionString + "\""; var appSettingsJsonFiles = context.Files.Where(f => f.Name.EndsWith("appsettings.json", StringComparison.OrdinalIgnoreCase)); foreach (var appSettingsJson in appSettingsJsonFiles) { - var appSettingsObject = JsonConvert.DeserializeObject(appSettingsJson.Content); - var oldConnectionString = appSettingsObject.ConnectionStrings.Default; - appSettingsJson.ReplaceText(oldConnectionString, newConnectionString); - } - } - - public class ConnectionStringModel - { - public string Default { get; set; } - } - - public class AppSettingsConnectionStringModel - { - public ConnectionStringModel ConnectionStrings { get; set; } + try + { + var jsonObject = JObject.Parse(appSettingsJson.Content); + var defaultConnectionString = ((Newtonsoft.Json.Linq.JContainer)jsonObject["ConnectionStrings"]).First.ToString(); - public string GetDefaultConnectionString() - { - return ConnectionStrings?.Default; + appSettingsJson.ReplaceText(defaultConnectionString, newConnectionString); + } + catch (Exception ex) + { + Console.WriteLine("Cannot change the connection string in " + appSettingsJson.Name + ". Error: " + ex.Message); + } } } } From 765d967a42d5c37e5c063226d8cd09b3f10b2e06 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Mon, 30 Mar 2020 19:25:54 +0300 Subject: [PATCH 04/11] Update ConnectionStringChangeStep.cs --- .../Building/Steps/ConnectionStringChangeStep.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs index d12bc45866..ea53868812 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs @@ -22,7 +22,9 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps try { var jsonObject = JObject.Parse(appSettingsJson.Content); - var defaultConnectionString = ((Newtonsoft.Json.Linq.JContainer)jsonObject["ConnectionStrings"]).First.ToString(); + var connectionStringContainer = (JContainer)jsonObject["ConnectionStrings"]; + var firstConnectionString = connectionStringContainer.First; + var defaultConnectionString = firstConnectionString.ToString(); appSettingsJson.ReplaceText(defaultConnectionString, newConnectionString); } From bc547cd05ac9fddffb3793d42fbbd85410adc08d Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Mon, 30 Mar 2020 20:37:33 +0300 Subject: [PATCH 05/11] Add ReadAllLinesAsync method. --- .../Steps/ConnectionStringChangeStep.cs | 24 +++++++++- .../Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs | 48 ++++++++++++++++++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs index ea53868812..491955650b 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Text; using System.Text.RegularExpressions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -21,7 +23,8 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps { try { - var jsonObject = JObject.Parse(appSettingsJson.Content); + var appSettingJsonContentWithoutBom = GetStringWithoutBom(appSettingsJson.Bytes); + var jsonObject = JObject.Parse(appSettingJsonContentWithoutBom); var connectionStringContainer = (JContainer)jsonObject["ConnectionStrings"]; var firstConnectionString = connectionStringContainer.First; var defaultConnectionString = firstConnectionString.ToString(); @@ -34,5 +37,24 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps } } } + + private static string GetStringWithoutBom(byte[] bytes) + { + if (bytes == null) + { + return null; + } + + var hasBom = bytes.Length >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF; + + if (hasBom) + { + return Encoding.UTF8.GetString(bytes, 3, bytes.Length - 3); + } + else + { + return Encoding.UTF8.GetString(bytes); + } + } } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs index 21ca7c2086..4c258dfbeb 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Text; using System.Threading.Tasks; @@ -73,6 +74,51 @@ namespace Volo.Abp.IO } } - //TODO: ReadAllLinesAsync + /// + /// Opens a text file, reads all lines of the file, and then closes the file. + /// + /// The file to open for reading. + /// Encoding of the file. Default is UTF8 + /// Specifies how the operating system should open a file. Default is Open + /// Defines constants for read, write, or read/write access to a file. Default is Read + /// Contains constants for controlling the kind of access other FileStream objects can have to the same file. Default is Read + /// Length of StreamReader buffer. Default is 4096. + /// Indicates FileStream options. Default is Asynchronous (The file is to be used for asynchronous reading.) and SequentialScan (The file is to be accessed sequentially from beginning to end.) + /// A string containing all lines of the file. + public static async Task ReadAllLinesAsync(string path, + Encoding encoding = null, + FileMode fileMode = FileMode.Open, + FileAccess fileAccess = FileAccess.Read, + FileShare fileShare = FileShare.Read, + int bufferSize = 4096, + FileOptions fileOptions = FileOptions.Asynchronous | FileOptions.SequentialScan) + { + if (encoding == null) + { + encoding = Encoding.UTF8; + } + + var lines = new List(); + + using (var stream = new FileStream( + path, + fileMode, + fileAccess, + fileShare, + bufferSize, + fileOptions)) + { + using (var reader = new StreamReader(stream, encoding)) + { + string line; + while ((line = await reader.ReadLineAsync()) != null) + { + lines.Add(line); + } + } + } + + return lines.ToArray(); + } } } \ No newline at end of file From 619d7665c40f4bcf2346b93d04d8452d7e29ccc8 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Mon, 30 Mar 2020 20:51:43 +0300 Subject: [PATCH 06/11] Add ConvertFromBytesWithoutBom to convert BOM encoded texts without BOM --- .../Volo/Abp/Text/StringHelper.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/Text/StringHelper.cs diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Text/StringHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Text/StringHelper.cs new file mode 100644 index 0000000000..c6be5db02b --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Text/StringHelper.cs @@ -0,0 +1,37 @@ +using System.Text; + +namespace Volo.Abp.Text +{ + public class StringHelper + { + /// + /// Converts a byte[] to string without BOM (byte order mark). + /// + /// The byte[] to be converted to string + /// The encoding to get string. Default is UTF8 + /// + public static string ConvertFromBytesWithoutBom(byte[] bytes, Encoding encoding = null) + { + if (bytes == null) + { + return null; + } + + if (encoding == null) + { + encoding = Encoding.UTF8; + } + + var hasBom = bytes.Length >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF; + + if (hasBom) + { + return encoding.GetString(bytes, 3, bytes.Length - 3); + } + else + { + return encoding.GetString(bytes); + } + } + } +} \ No newline at end of file From b591a55bf28c3f7c829b35336d4920610d6d3599 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Mon, 30 Mar 2020 20:52:11 +0300 Subject: [PATCH 07/11] Add ReadFileWithoutBom to get content when the file encoded with Byte Order Mark. --- .../src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs index 4c258dfbeb..a945784352 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs @@ -4,6 +4,7 @@ using System.IO; using System.Text; using System.Threading.Tasks; using JetBrains.Annotations; +using Volo.Abp.Text; namespace Volo.Abp.IO { @@ -120,5 +121,17 @@ namespace Volo.Abp.IO return lines.ToArray(); } + + /// + /// Opens a text file, reads content without BOM + /// + /// The file to open for reading. + /// A string containing all lines of the file. + public static async Task ReadFileWithoutBomAsync(string path) + { + var content = await ReadAllBytesAsync(path); + + return StringHelper.ConvertFromBytesWithoutBom(content); + } } } \ No newline at end of file From 0b2546ed04838fdcf84ecb2fccdec7d6f9f19bbb Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Mon, 30 Mar 2020 21:09:54 +0300 Subject: [PATCH 08/11] closes #3208 --- .../Steps/ConnectionStringChangeStep.cs | 70 ++++++++++++------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs index 491955650b..02bc8219b8 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs @@ -7,29 +7,64 @@ using System.Text.RegularExpressions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Volo.Abp.Cli.ProjectBuilding.Files; +using Volo.Abp.Text; namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps { public class ConnectionStringChangeStep : ProjectBuildPipelineStep { + private const string DefaultConnectionStringKey = "Default"; + public override void Execute(ProjectBuildContext context) { - var newConnectionString = "\"Default\": \"" + context.BuildArgs.ConnectionString + "\""; - var appSettingsJsonFiles = context.Files.Where(f => - f.Name.EndsWith("appsettings.json", StringComparison.OrdinalIgnoreCase)); + f.Name.EndsWith("appsettings.json", StringComparison.OrdinalIgnoreCase)) + .ToArray(); + + if (!appSettingsJsonFiles.Any()) + { + return; + } + + var newConnectionString = $"\"{DefaultConnectionStringKey}: \"{context.BuildArgs.ConnectionString}\""; foreach (var appSettingsJson in appSettingsJsonFiles) { try { - var appSettingJsonContentWithoutBom = GetStringWithoutBom(appSettingsJson.Bytes); + var appSettingJsonContentWithoutBom = StringHelper.ConvertFromBytesWithoutBom(appSettingsJson.Bytes); var jsonObject = JObject.Parse(appSettingJsonContentWithoutBom); - var connectionStringContainer = (JContainer)jsonObject["ConnectionStrings"]; - var firstConnectionString = connectionStringContainer.First; - var defaultConnectionString = firstConnectionString.ToString(); - appSettingsJson.ReplaceText(defaultConnectionString, newConnectionString); + var connectionStringContainer = (JContainer)jsonObject?["ConnectionStrings"]; + if (connectionStringContainer == null) + { + continue; + } + + if (!connectionStringContainer.Any()) + { + continue; + } + + var connectionStrings = connectionStringContainer.ToList(); + + foreach (var connectionString in connectionStrings) + { + var property = ((JProperty)connectionString); + var connectionStringName = property.Name; + + if (connectionStringName == DefaultConnectionStringKey) + { + var defaultConnectionString = property.ToString(); + if (defaultConnectionString == null) + { + continue; + } + + appSettingsJson.ReplaceText(defaultConnectionString, newConnectionString); + break; + } + } } catch (Exception ex) { @@ -37,24 +72,5 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps } } } - - private static string GetStringWithoutBom(byte[] bytes) - { - if (bytes == null) - { - return null; - } - - var hasBom = bytes.Length >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF; - - if (hasBom) - { - return Encoding.UTF8.GetString(bytes, 3, bytes.Length - 3); - } - else - { - return Encoding.UTF8.GetString(bytes); - } - } } } From 990c74f00086df4578af1823a05db147d34ef24a Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Mon, 30 Mar 2020 21:17:15 +0300 Subject: [PATCH 09/11] add missing string closer char --- .../Building/Steps/ConnectionStringChangeStep.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs index 02bc8219b8..b1e118ee8b 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ConnectionStringChangeStep.cs @@ -26,7 +26,7 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps return; } - var newConnectionString = $"\"{DefaultConnectionStringKey}: \"{context.BuildArgs.ConnectionString}\""; + var newConnectionString = $"\"{DefaultConnectionStringKey}\": \"{context.BuildArgs.ConnectionString}\""; foreach (var appSettingsJson in appSettingsJsonFiles) { From 2a5fdf7f8b7017c8b256f34649f68c2696da7921 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Tue, 31 Mar 2020 08:29:41 +0300 Subject: [PATCH 10/11] chore: remove unnecessary package --- npm/ng-packs/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index 5092ae340c..9e81892e58 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -77,7 +77,6 @@ "ngxs-reset-plugin": "^1.2.0", "ngxs-schematic": "^1.1.9", "prettier": "^1.18.2", - "primeicons": "^2.0.0", "protractor": "~5.4.0", "rxjs": "~6.4.0", "snq": "^1.0.3", From 81600ceb1daf6ef1093e2d004f790abf5a6caf89 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Tue, 31 Mar 2020 10:06:06 +0300 Subject: [PATCH 11/11] chore: remove linked list --- .../core/src/lib/tests/linked-list.spec.ts | 1532 ----------------- .../packages/core/src/lib/utils/index.ts | 1 - .../core/src/lib/utils/linked-list.ts | 396 ----- 3 files changed, 1929 deletions(-) delete mode 100644 npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts delete mode 100644 npm/ng-packs/packages/core/src/lib/utils/linked-list.ts diff --git a/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts deleted file mode 100644 index 3ad9eeda63..0000000000 --- a/npm/ng-packs/packages/core/src/lib/tests/linked-list.spec.ts +++ /dev/null @@ -1,1532 +0,0 @@ -import { LinkedList, ListNode } from '../utils/linked-list'; - -describe('Linked List (Doubly)', () => { - let list: LinkedList; - - beforeEach(() => (list = new LinkedList())); - - describe('#length', () => { - it('should initially be 0', () => { - expect(list.length).toBe(0); - }); - }); - - describe('#head', () => { - it('should initially be undefined', () => { - expect(list.head).toBeUndefined(); - }); - }); - - describe('#tail', () => { - it('should initially be undefined', () => { - expect(list.tail).toBeUndefined(); - }); - }); - - describe('#add', () => { - describe('#head', () => { - it('should add node to the head of the list', () => { - list.addHead('a'); - - // "a" - - expect(list.head.value).toBe('a'); - expect(list.tail.value).toBe('a'); - }); - - it('should create reference to previous and next nodes', () => { - list.add('a').head(); - list.add('b').head(); - list.add('c').head(); - - // "c" <-> "b" <-> "a" - - expect(list.length).toBe(3); - expect(list.head.value).toBe('c'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('a'); - expect(list.head.previous).toBeUndefined(); - expect(list.tail.value).toBe('a'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('c'); - expect(list.tail.next).toBeUndefined(); - }); - }); - - describe('#tail', () => { - it('should add node to the tail of the list', () => { - list.addTail('a'); - - // "a" - - expect(list.head.value).toBe('a'); - expect(list.tail.value).toBe('a'); - expect(list.tail.next).toBeUndefined(); - }); - - it('should create reference to previous and next nodes', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.head.previous).toBeUndefined(); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - expect(list.tail.next).toBeUndefined(); - }); - }); - - describe('#after', () => { - it('should add a node after node with given value', () => { - list.add('a').tail(); - - // "a" - - list.add('b').after('a'); - list.add('c').after('b'); - - // "a" <-> "b" <-> "c" - - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - }); - - it('should add a node to tail if given value is not found', () => { - list.add('a').tail(); - - // "a" - - list.add('b').after('x'); - - // "a" <-> "b" - - expect(list.length).toBe(2); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.tail.value).toBe('b'); - expect(list.tail.previous.value).toBe('a'); - }); - - it('should be able to receive a custom compareFn', () => { - list.add({ x: 1 }).tail(); - list.add({ x: 2 }).tail(); - list.add({ x: 3 }).tail(); - - // {"x":1} <-> {"x":2} <-> {"x":3} - - list.add({ x: 0 }).after({ x: 1 }, (v1: X, v2: X) => v1.x === v2.x); - - // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":3} - - expect(list.length).toBe(4); - expect(list.head.value.x).toBe(1); - expect(list.head.next.value.x).toBe(0); - expect(list.head.next.next.value.x).toBe(2); - expect(list.head.next.next.next.value.x).toBe(3); - expect(list.tail.value.x).toBe(3); - expect(list.tail.previous.value.x).toBe(2); - expect(list.tail.previous.previous.value.x).toBe(0); - expect(list.tail.previous.previous.previous.value.x).toBe(1); - }); - }); - - describe('#before', () => { - it('should add a node before node with given value', () => { - list.add('c').tail(); - - // "c" - - list.add('b').before('c'); - list.add('a').before('b'); - - // "a" <-> "b" <-> "c" - - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - }); - - it('should add a node to head if given value is not found', () => { - list.add('b').tail(); - - // "a" - - list.add('a').before('x'); - - // "a" <-> "b" - - expect(list.length).toBe(2); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.tail.value).toBe('b'); - expect(list.tail.previous.value).toBe('a'); - }); - - it('should be able to receive a custom compareFn', () => { - list.add({ x: 1 }).tail(); - list.add({ x: 2 }).tail(); - list.add({ x: 3 }).tail(); - - // {"x":1} <-> {"x":2} <-> {"x":3} - - list.add({ x: 0 }).before({ x: 2 }, (v1: X, v2: X) => v1.x === v2.x); - - // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":3} - - expect(list.length).toBe(4); - expect(list.head.value.x).toBe(1); - expect(list.head.next.value.x).toBe(0); - expect(list.head.next.next.value.x).toBe(2); - expect(list.head.next.next.next.value.x).toBe(3); - expect(list.tail.value.x).toBe(3); - expect(list.tail.previous.value.x).toBe(2); - expect(list.tail.previous.previous.value.x).toBe(0); - expect(list.tail.previous.previous.previous.value.x).toBe(1); - }); - }); - - describe('#byIndex', () => { - it('should add a node at given index', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - list.add('x').byIndex(1); - - // "a" <-> "x" <-> "b" <-> "c" - - list.add('y').byIndex(3); - - // "a" <-> "x" <-> "b" <-> "y" <-> "c" - - expect(list.length).toBe(5); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('x'); - expect(list.head.next.next.value).toBe('b'); - expect(list.head.next.next.next.value).toBe('y'); - expect(list.head.next.next.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('y'); - expect(list.tail.previous.previous.value).toBe('b'); - expect(list.tail.previous.previous.previous.value).toBe('x'); - expect(list.tail.previous.previous.previous.previous.value).toBe('a'); - }); - - it('should add a node to head if given index is zero', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - list.add('x').byIndex(0); - - // "x" <-> "a" <-> "b" <-> "c" - - expect(list.length).toBe(4); - expect(list.head.value).toBe('x'); - expect(list.head.next.value).toBe('a'); - expect(list.head.next.next.value).toBe('b'); - expect(list.head.next.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - expect(list.tail.previous.previous.previous.value).toBe('x'); - }); - - it('should add a node to tail if given index more than size', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - list.add('x').byIndex(4); - - // "a" <-> "b" <-> "c" <-> "x" - - expect(list.length).toBe(4); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.head.next.next.next.value).toBe('x'); - expect(list.tail.value).toBe('x'); - expect(list.tail.previous.value).toBe('c'); - expect(list.tail.previous.previous.value).toBe('b'); - expect(list.tail.previous.previous.previous.value).toBe('a'); - }); - - it('should be able to add a node at given index counting from right to left', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - list.add('x').byIndex(-1); - - // "a" <-> "b" <-> "x" <-> "c" - - expect(list.length).toBe(4); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('x'); - expect(list.head.next.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('x'); - expect(list.tail.previous.previous.value).toBe('b'); - expect(list.tail.previous.previous.previous.value).toBe('a'); - }); - - it('should add a node to head if given index is less than minus size', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - list.add('x').byIndex(-4); - - // "x" <-> "a" <-> "b" <-> "c" - - expect(list.length).toBe(4); - expect(list.head.value).toBe('x'); - expect(list.head.next.value).toBe('a'); - expect(list.head.next.next.value).toBe('b'); - expect(list.head.next.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - expect(list.tail.previous.previous.previous.value).toBe('x'); - }); - }); - }); - - describe('#addMany', () => { - describe('#head', () => { - it('should add multiple nodes to the head of the list', () => { - list.add('x').head(); - - // "x" - - list.addMany(['a', 'b', 'c']).head(); - - // "a" <-> "b" <-> "c" <-> "x" - - expect(list.length).toBe(4); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.head.next.next.next.value).toBe('x'); - expect(list.head.previous).toBeUndefined(); - expect(list.tail.value).toBe('x'); - expect(list.tail.previous.value).toBe('c'); - expect(list.tail.previous.previous.value).toBe('b'); - expect(list.tail.previous.previous.previous.value).toBe('a'); - expect(list.tail.next).toBeUndefined(); - }); - }); - - describe('#tail', () => { - it('should add multiple nodes to the tail of the list', () => { - list.add('x').tail(); - - // "x" - - list.addMany(['a', 'b', 'c']).tail(); - - // "x" <-> "a" <-> "b" <-> "c" - - expect(list.length).toBe(4); - expect(list.head.value).toBe('x'); - expect(list.head.next.value).toBe('a'); - expect(list.head.next.next.value).toBe('b'); - expect(list.head.next.next.next.value).toBe('c'); - expect(list.head.previous).toBeUndefined(); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - expect(list.tail.previous.previous.previous.value).toBe('x'); - expect(list.tail.next).toBeUndefined(); - }); - }); - - describe('#after', () => { - it('should add multiple nodes after node with given value', () => { - list.add('a').tail(); - - // "a" - - list.addMany(['b', 'c']).after('a'); - - // "a" <-> "b" <-> "c" - - list.addMany(['x', 'y', 'z']).after('b'); - - // "a" <-> "b" <-> "x" <-> "y" <-> "z" <-> "c" - - expect(list.length).toBe(6); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('x'); - expect(list.head.next.next.next.value).toBe('y'); - expect(list.head.next.next.next.next.value).toBe('z'); - expect(list.head.next.next.next.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('z'); - expect(list.tail.previous.previous.value).toBe('y'); - expect(list.tail.previous.previous.previous.value).toBe('x'); - expect(list.tail.previous.previous.previous.previous.value).toBe('b'); - expect(list.tail.previous.previous.previous.previous.previous.value).toBe('a'); - }); - - it('should add multiple nodes to tail if given value is not found', () => { - list.add('a').tail(); - - // "a" - - list.addMany(['b', 'c']).after('x'); - - // "a" <-> "b" <-> "c" - - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - }); - - it('should be able to receive a custom compareFn', () => { - list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }]).tail(); - - // {"x":1} <-> {"x":2} <-> {"x":3} - - list.addMany([{ x: 4 }, { x: 5 }]).after({ x: 1 }, (v1: X, v2: X) => v1.x === v2.x); - - // {"x":1} <-> {"x":4} <-> {"x":5} <-> {"x":2} <-> {"x":3} - - expect(list.length).toBe(5); - expect(list.head.value.x).toBe(1); - expect(list.head.next.value.x).toBe(4); - expect(list.head.next.next.value.x).toBe(5); - expect(list.head.next.next.next.value.x).toBe(2); - expect(list.head.next.next.next.next.value.x).toBe(3); - expect(list.tail.value.x).toBe(3); - expect(list.tail.previous.value.x).toBe(2); - expect(list.tail.previous.previous.value.x).toBe(5); - expect(list.tail.previous.previous.previous.value.x).toBe(4); - expect(list.tail.previous.previous.previous.previous.value.x).toBe(1); - }); - - it('should not change the list when empty array given as value', () => { - list.add('a').tail(); - - // "a" - - list.addMany([]).after('a'); - - // "a" - - expect(list.length).toBe(1); - expect(list.head.value).toBe('a'); - expect(list.tail.value).toBe('a'); - }); - }); - - describe('#before', () => { - it('should add multiple nodes before node with given value', () => { - list.add('c').tail(); - - // "c" - - list.addMany(['a', 'b']).before('c'); - - // "a" <-> "b" <-> "c" - - list.addMany(['x', 'y', 'z']).before('b'); - - // "a" <-> "x" <-> "y" <-> "z" <-> "b" <-> "c" - - expect(list.length).toBe(6); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('x'); - expect(list.head.next.next.value).toBe('y'); - expect(list.head.next.next.next.value).toBe('z'); - expect(list.head.next.next.next.next.value).toBe('b'); - expect(list.head.next.next.next.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('z'); - expect(list.tail.previous.previous.previous.value).toBe('y'); - expect(list.tail.previous.previous.previous.previous.value).toBe('x'); - expect(list.tail.previous.previous.previous.previous.previous.value).toBe('a'); - }); - - it('should add multiple nodes to head if given value is not found', () => { - list.add('c').tail(); - - // "c" - - list.addMany(['a', 'b']).before('x'); - - // "a" <-> "b" <-> "c" - - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - }); - - it('should be able to receive a custom compareFn', () => { - list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }]).tail(); - - // {"x":1} <-> {"x":2} <-> {"x":3} - - list.addMany([{ x: 4 }, { x: 5 }]).before({ x: 3 }, (v1: X, v2: X) => v1.x === v2.x); - - // {"x":1} <-> {"x":2} <-> {"x":4} <-> {"x":5} <-> {"x":3} - - expect(list.length).toBe(5); - expect(list.head.value.x).toBe(1); - expect(list.head.next.value.x).toBe(2); - expect(list.head.next.next.value.x).toBe(4); - expect(list.head.next.next.next.value.x).toBe(5); - expect(list.head.next.next.next.next.value.x).toBe(3); - expect(list.tail.value.x).toBe(3); - expect(list.tail.previous.value.x).toBe(5); - expect(list.tail.previous.previous.value.x).toBe(4); - expect(list.tail.previous.previous.previous.value.x).toBe(2); - expect(list.tail.previous.previous.previous.previous.value.x).toBe(1); - }); - - it('should not change the list when empty array given as value', () => { - list.add('a').tail(); - - // "a" - - list.addMany([]).before('a'); - - // "a" - - expect(list.length).toBe(1); - expect(list.head.value).toBe('a'); - expect(list.tail.value).toBe('a'); - }); - }); - - describe('#byIndex', () => { - it('should add multiple nodes starting from given index', () => { - list.addMany(['a', 'b', 'c']).tail(); - - // "a" <-> "b" <-> "c" - - list.addMany(['x', 'y']).byIndex(1); - - // "a" <-> "x" <-> "y" <-> "b" <-> "c" - - list.addMany(['z']).byIndex(4); - - // "a" <-> "x" <-> "y" <-> "b" <-> "z" <-> "c" - - expect(list.length).toBe(6); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('x'); - expect(list.head.next.next.value).toBe('y'); - expect(list.head.next.next.next.value).toBe('b'); - expect(list.head.next.next.next.next.value).toBe('z'); - expect(list.head.next.next.next.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('z'); - expect(list.tail.previous.previous.value).toBe('b'); - expect(list.tail.previous.previous.previous.value).toBe('y'); - expect(list.tail.previous.previous.previous.previous.value).toBe('x'); - expect(list.tail.previous.previous.previous.previous.previous.value).toBe('a'); - }); - - it('should add multiple nodes to head if given index is zero', () => { - list.addMany(['a', 'b', 'c']).tail(); - - // "a" <-> "b" <-> "c" - - list.addMany(['x', 'y']).byIndex(0); - - // "x" <-> "y" <-> "a" <-> "b" <-> "c" - - expect(list.length).toBe(5); - expect(list.head.value).toBe('x'); - expect(list.head.next.value).toBe('y'); - expect(list.head.next.next.value).toBe('a'); - expect(list.head.next.next.next.value).toBe('b'); - expect(list.head.next.next.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - expect(list.tail.previous.previous.previous.value).toBe('y'); - expect(list.tail.previous.previous.previous.previous.value).toBe('x'); - }); - - it('should add multiple nodes to tail if given index more than size', () => { - list.addMany(['a', 'b', 'c']).tail(); - - // "a" <-> "b" <-> "c" - - list.addMany(['x', 'y']).byIndex(4); - - // "a" <-> "b" <-> "c" <-> "x" <-> "y" - - expect(list.length).toBe(5); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.head.next.next.next.value).toBe('x'); - expect(list.head.next.next.next.next.value).toBe('y'); - expect(list.tail.value).toBe('y'); - expect(list.tail.previous.value).toBe('x'); - expect(list.tail.previous.previous.value).toBe('c'); - expect(list.tail.previous.previous.previous.value).toBe('b'); - expect(list.tail.previous.previous.previous.previous.value).toBe('a'); - }); - - it('should be able to add multiple nodes at given index counting from right to left', () => { - list.addMany(['a', 'b', 'c']).tail(); - - // "a" <-> "b" <-> "c" - - list.addMany(['x', 'y']).byIndex(-1); - - // "a" <-> "b" <-> "x" <-> "y" <-> "c" - - expect(list.length).toBe(5); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('x'); - expect(list.head.next.next.next.value).toBe('y'); - expect(list.head.next.next.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('y'); - expect(list.tail.previous.previous.value).toBe('x'); - expect(list.tail.previous.previous.previous.value).toBe('b'); - expect(list.tail.previous.previous.previous.previous.value).toBe('a'); - }); - - it('should add a node to head if given index is less than minus size', () => { - list.addMany(['a', 'b', 'c']).tail(); - - // "a" <-> "b" <-> "c" - - list.addMany(['x', 'y']).byIndex(-4); - - // "x" <-> "y" <-> "a" <-> "b" <-> "c" - - expect(list.length).toBe(5); - expect(list.head.value).toBe('x'); - expect(list.head.next.value).toBe('y'); - expect(list.head.next.next.value).toBe('a'); - expect(list.head.next.next.next.value).toBe('b'); - expect(list.head.next.next.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - expect(list.tail.previous.previous.previous.value).toBe('y'); - expect(list.tail.previous.previous.previous.previous.value).toBe('x'); - }); - }); - }); - - describe('#find', () => { - it('should return the first node found based on given predicate', () => { - list.add('a').tail(); - list.add('x').tail(); - list.add('b').tail(); - list.add('x').tail(); - list.add('c').tail(); - - // "a" <-> "x" <-> "b" <-> "x" <-> "c" - - const found1 = list.find(node => node.previous && node.previous.value === 'a'); - - expect(found1.value).toBe('x'); - expect(found1.previous.value).toBe('a'); - expect(found1.next.value).toBe('b'); - - // "a" <-> "x" <-> "b" <-> "x" <-> "c" - - const found2 = list.find(node => node.next && node.next.value === 'c'); - - expect(found2.value).toBe('x'); - expect(found2.previous.value).toBe('b'); - expect(found2.next.value).toBe('c'); - }); - - it('should return undefined when list is empty', () => { - const found = list.find(node => node.value === 'x'); - - expect(found).toBeUndefined(); - }); - - it('should return undefined when predicate finds no match', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - const found = list.find(node => node.value === 'x'); - - expect(found).toBeUndefined(); - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - }); - }); - - describe('#findIndex', () => { - it('should return the index of the first node found based on given predicate', () => { - list.add('a').tail(); - list.add('x').tail(); - list.add('b').tail(); - list.add('x').tail(); - list.add('c').tail(); - - // "a" <-> "x" <-> "b" <-> "x" <-> "c" - - const index1 = list.findIndex(node => node.previous && node.previous.value === 'a'); - - expect(index1).toBe(1); - - // "a" <-> "x" <-> "b" <-> "x" <-> "c" - - const index2 = list.findIndex(node => node.next && node.next.value === 'c'); - - expect(index2).toBe(3); - }); - - it('should return -1 when list is empty', () => { - const index = list.findIndex(node => node.value === 'x'); - - expect(index).toBe(-1); - }); - - it('should return -1 when no match is found', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - const index = list.findIndex(node => node.value === 'x'); - - expect(index).toBe(-1); - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - }); - }); - - describe('#forEach', () => { - it('should call given function for each node of the list', () => { - const a = list.add('a').tail(); - const b = list.add('b').tail(); - const c = list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - const spy = jest.fn(); - list.forEach(spy); - - expect(spy.mock.calls).toEqual([ - [a, 0, list], - [b, 1, list], - [c, 2, list], - ]); - }); - - it('should not call given function when list is empty', () => { - const spy = jest.fn(); - list.forEach(spy); - - expect(spy).not.toHaveBeenCalled(); - }); - }); - - describe('#drop', () => { - describe('#head', () => { - it('should return undefined when there is no head', () => { - const dropped = list.drop().head(); - expect(dropped).toBeUndefined(); - }); - - it('should remove the node from the head of the list', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - list.drop().head(); - - // "b" <-> "c" - - expect(list.length).toBe(2); - expect(list.head.value).toBe('b'); - expect(list.head.next.value).toBe('c'); - expect(list.head.previous).toBeUndefined(); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.next).toBeUndefined(); - - // "b" <-> "c" - - list.drop().head(); - list.drop().head(); - - expect(list.length).toBe(0); - expect(list.head).toBeUndefined(); - expect(list.tail).toBeUndefined(); - }); - }); - - describe('#tail', () => { - it('should return undefined when there is no tail', () => { - const dropped = list.drop().tail(); - expect(dropped).toBeUndefined(); - }); - - it('should remove the node from the tail of the list', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - list.drop().tail(); - - // "a" <-> "b" - - expect(list.length).toBe(2); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.previous).toBeUndefined(); - expect(list.tail.value).toBe('b'); - expect(list.tail.previous.value).toBe('a'); - expect(list.tail.next).toBeUndefined(); - - // "a" <-> "b" - - list.drop().tail(); - list.drop().tail(); - - expect(list.length).toBe(0); - expect(list.head).toBeUndefined(); - expect(list.tail).toBeUndefined(); - }); - }); - - describe('#byIndex', () => { - it('should remove the node at given index', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - list.add('d').tail(); - list.add('e').tail(); - - // "a" <-> "b" <-> "c" <-> "d" <-> "e" - - list.drop().byIndex(1); - - // "a" <-> "c" <-> "d" <-> "e" - - list.drop().byIndex(-2); - - // "a" <-> "c" <-> "e" - - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('c'); - expect(list.head.next.next.value).toBe('e'); - expect(list.tail.value).toBe('e'); - expect(list.tail.previous.value).toBe('c'); - expect(list.tail.previous.previous.value).toBe('a'); - - // "a" <-> "c" <-> "e" - - list.drop().byIndex(2); - list.drop().byIndex(1); - list.drop().byIndex(0); - - expect(list.length).toBe(0); - expect(list.head).toBeUndefined(); - expect(list.tail).toBeUndefined(); - }); - - it('should return undefined when list is empty', () => { - const dropped = list.drop().byIndex(0); - expect(dropped).toBeUndefined(); - }); - - it('should return undefined when given index does not exist', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - const dropped1 = list.drop().byIndex(4); - - // "a" <-> "b" <-> "c" - - expect(dropped1).toBeUndefined(); - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - - // "a" <-> "b" <-> "c" - - const dropped2 = list.drop().byIndex(-4); - - // "a" <-> "b" <-> "c" - - expect(dropped2).toBeUndefined(); - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - }); - }); - - describe('#byValue', () => { - it('should remove the first node with given value', () => { - list.add('a').tail(); - list.add('x').tail(); - list.add('b').tail(); - list.add('x').tail(); - list.add('c').tail(); - - // "a" <-> "x" <-> "b" <-> "x" <-> "c" - - list.drop().byValue('x'); - - // "a" <-> "b" <-> "x" <-> "c" - - expect(list.length).toBe(4); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('x'); - expect(list.head.next.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('x'); - expect(list.tail.previous.previous.value).toBe('b'); - expect(list.tail.previous.previous.previous.value).toBe('a'); - - // "a" <-> "b" <-> "x" <-> "c" - - list.drop().byValue('x'); - - // "a" <-> "b" <-> "c" - - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - }); - - it('should be able to receive a custom compareFn', () => { - list.add({ x: 1 }).tail(); - list.add({ x: 2 }).tail(); - list.add({ x: 3 }).tail(); - - // {"x":1} <-> {"x":2} <-> {"x":3} - - list.drop().byValue({ x: 2 }, (v1: X, v2: X) => v1.x === v2.x); - - // {"x":1} <-> {"x":3} - - expect(list.length).toBe(2); - expect(list.head.value.x).toBe(1); - expect(list.head.next.value.x).toBe(3); - expect(list.tail.value.x).toBe(3); - expect(list.tail.previous.value.x).toBe(1); - }); - - it('should return undefined when list is empty', () => { - const dropped = list.drop().byValue('x'); - expect(dropped).toBeUndefined(); - }); - - it('should return undefined when given value is not found', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - const node = list.drop().byValue('x'); - - // "a" <-> "b" <-> "c" - - expect(node).toBeUndefined(); - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - }); - }); - - describe('#byValueAll', () => { - it('should remove all nodes with given value', () => { - list.add('a').tail(); - list.add('x').tail(); - list.add('b').tail(); - list.add('x').tail(); - list.add('c').tail(); - - // "a" <-> "x" <-> "b" <-> "x" <-> "c" - - const dropped = list.drop().byValueAll('x'); - - // "a" <-> "b" <-> "c" - - expect(dropped.length).toBe(2); - expect(dropped[0].value).toEqual('x'); - expect(dropped[0].previous.value).toEqual('a'); - expect(dropped[0].next.value).toEqual('b'); - expect(dropped[1].value).toEqual('x'); - expect(dropped[1].previous.value).toEqual('b'); - expect(dropped[1].next.value).toEqual('c'); - - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - }); - - it('should be able to receive a custom compareFn', () => { - list.add({ x: 1 }).tail(); - list.add({ x: 0 }).tail(); - list.add({ x: 2 }).tail(); - list.add({ x: 0 }).tail(); - list.add({ x: 3 }).tail(); - - // {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} - - const dropped = list.drop().byValueAll({ x: 0 }, (v1: X, v2: X) => v1.x === v2.x); - - // {"x":1} <-> {"x":2} <-> {"x":3} - - expect(dropped.length).toBe(2); - expect(dropped[0].value.x).toEqual(0); - expect(dropped[0].previous.value.x).toEqual(1); - expect(dropped[0].next.value.x).toEqual(2); - expect(dropped[1].value.x).toEqual(0); - expect(dropped[1].previous.value.x).toEqual(2); - expect(dropped[1].next.value.x).toEqual(3); - - expect(list.length).toBe(3); - expect(list.head.value.x).toBe(1); - expect(list.head.next.value.x).toBe(2); - expect(list.head.next.next.value.x).toBe(3); - expect(list.tail.value.x).toBe(3); - expect(list.tail.previous.value.x).toBe(2); - expect(list.tail.previous.previous.value.x).toBe(1); - }); - - it('should return empty array when list is empty', () => { - const dropped = list.drop().byValueAll('x'); - expect(dropped).toEqual([]); - }); - - it('should return empty array when given value is not found', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - const dropped = list.drop().byValueAll('x'); - - // "a" <-> "b" <-> "c" - - expect(dropped).toEqual([]); - - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - }); - }); - }); - - describe('#dropMany', () => { - describe('#head', () => { - it('should return empty array when there is no head', () => { - const dropped = list.dropMany(1).head(); - expect(dropped).toEqual([]); - }); - - it('should remove multiple nodes from the head of the list', () => { - list.addMany(['a', 'b', 'c', 'd', 'e']).tail(); - - // "a" <-> "b" <-> "c" <-> "d" <-> "e" - - list.dropMany(3).head(); - - // "d" <-> "e" - - expect(list.length).toBe(2); - expect(list.head.value).toBe('d'); - expect(list.head.next.value).toBe('e'); - expect(list.head.previous).toBeUndefined(); - expect(list.tail.value).toBe('e'); - expect(list.tail.previous.value).toBe('d'); - expect(list.tail.next).toBeUndefined(); - }); - - it('should not change the list when count is less than or equal to zero', () => { - list.add('a').tail(); - - // "a" - - list.dropMany(0).head(); - - // "a" - - list.dropMany(-1).head(); - - // "a" - - expect(list.length).toBe(1); - expect(list.head.value).toBe('a'); - expect(list.tail.value).toBe('a'); - }); - }); - - describe('#tail', () => { - it('should return empty array when there is no tail', () => { - const dropped = list.dropMany(1).tail(); - expect(dropped).toEqual([]); - }); - - it('should remove multiple nodes from the tail of the list', () => { - list.addMany(['a', 'b', 'c', 'd', 'e']).tail(); - - // "a" <-> "b" <-> "c" <-> "d" <-> "e" - - list.dropMany(3).tail(); - - // "a" <-> "b" - - expect(list.length).toBe(2); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.previous).toBeUndefined(); - expect(list.tail.value).toBe('b'); - expect(list.tail.previous.value).toBe('a'); - expect(list.tail.next).toBeUndefined(); - }); - - it('should not change the list when count is less than or equal to zero', () => { - list.add('a').tail(); - - // "a" - - list.dropMany(0).tail(); - - // "a" - - list.dropMany(-1).tail(); - - // "a" - - expect(list.length).toBe(1); - expect(list.head.value).toBe('a'); - expect(list.tail.value).toBe('a'); - }); - }); - - describe('#byIndex', () => { - it('should remove multiple nodes starting from given index', () => { - list.addMany(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']).tail(); - - // "a" <-> "b" <-> "c" <-> "d" <-> "e" <-> "f" <-> "g" <-> "h" - - list.dropMany(2).byIndex(1); - - // "a" <-> "d" <-> "e" <-> "f" <-> "g" <-> "h" - - list.dropMany(2).byIndex(-3); - - // "a" <-> "d" <-> "e" <-> "h" - - expect(list.length).toBe(4); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('d'); - expect(list.head.next.next.value).toBe('e'); - expect(list.head.next.next.next.value).toBe('h'); - expect(list.tail.value).toBe('h'); - expect(list.tail.previous.value).toBe('e'); - expect(list.tail.previous.previous.value).toBe('d'); - expect(list.tail.previous.previous.previous.value).toBe('a'); - - list.dropMany(4).byIndex(0); - - expect(list.length).toBe(0); - expect(list.head).toBeUndefined(); - expect(list.tail).toBeUndefined(); - }); - - it('should return empty array when list is empty', () => { - const dropped = list.dropMany(1).byIndex(0); - expect(dropped).toEqual([]); - }); - - it('should return empty array when given index does not exist', () => { - list.addMany(['a', 'b', 'c']).tail(); - - // "a" <-> "b" <-> "c" - - const dropped = list.dropMany(3).byIndex(4); - - // "a" <-> "b" <-> "c" - - expect(dropped).toEqual([]); - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - }); - - it('should remove from start when given index is less than minus size', () => { - list.addMany(['a', 'b', 'c', 'd', 'e']).tail(); - - // "a" <-> "b" <-> "c" <-> "d" <-> "e" - - list.dropMany(2).byIndex(-9); - - // "c" <-> "d" <-> "e" - - expect(list.length).toBe(3); - expect(list.head.value).toBe('c'); - expect(list.head.next.value).toBe('d'); - expect(list.head.next.next.value).toBe('e'); - expect(list.tail.value).toBe('e'); - expect(list.tail.previous.value).toBe('d'); - expect(list.tail.previous.previous.value).toBe('c'); - }); - - it('should remove from end when given index + count is larger than or equal to size', () => { - list.addMany(['a', 'b', 'c', 'd', 'e']).tail(); - - // "a" <-> "b" <-> "c" <-> "d" <-> "e" - - list.dropMany(2).byIndex(3); - - // "a" <-> "b" <-> "c" - - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - - // "a" <-> "b" <-> "c" - - list.dropMany(9).byIndex(1); - - // "a" - - expect(list.length).toBe(1); - expect(list.head.value).toBe('a'); - expect(list.tail.value).toBe('a'); - }); - - it('should not change the list when count is less than or equal to zero', () => { - list.add('a').tail(); - - // "a" - - list.dropMany(0).byIndex(0); - - // "a" - - list.dropMany(-1).byIndex(0); - - // "a" - - expect(list.length).toBe(1); - expect(list.head.value).toBe('a'); - expect(list.tail.value).toBe('a'); - }); - }); - }); - - describe('#get', () => { - it('should return node at given index', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - const node = list.get(1); - - expect(node.value).toBe('b'); - expect(node.previous.value).toBe('a'); - expect(node.next.value).toBe('c'); - }); - - it('should return undefined when list is empty', () => { - const node = list.get(1); - - expect(node).toBeUndefined(); - }); - - it('should return undefined when predicate finds no match', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - const node1 = list.get(4); - - expect(node1).toBeUndefined(); - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - - // "a" <-> "b" <-> "c" - - const node2 = list.get(-1); - - expect(node2).toBeUndefined(); - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - }); - }); - - describe('#indexOf', () => { - it('should return the index of the first node found based on given value', () => { - list.add('a').tail(); - list.add('x').tail(); - list.add('b').tail(); - list.add('x').tail(); - list.add('c').tail(); - - // "a" <-> "x" <-> "b" <-> "x" <-> "c" - - const index1 = list.indexOf('x'); - - expect(index1).toBe(1); - - // "a" <-> "x" <-> "b" <-> "x" <-> "c" - - let timesFound = 0; - const index2 = list.indexOf('x', (v1: string, v2: string) => { - if (timesFound > 1) return false; - - timesFound += Number(v1 === v2); - - return timesFound > 1; - }); - - expect(index2).toBe(3); - }); - - it('should be able to receive a custom compareFn', () => { - list.add({ x: 1 }).tail(); - list.add({ x: 2 }).tail(); - list.add({ x: 3 }).tail(); - - // {"x":1} <-> {"x":2} <-> {"x":3} - - const index = list.indexOf({ x: 2 }, (v1: X, v2: X) => v1.x === v2.x); - - expect(index).toBe(1); - }); - - it('should return -1 when list is empty', () => { - const index = list.indexOf('x'); - - expect(index).toBe(-1); - }); - - it('should return -1 when no match is found', () => { - list.add('a').tail(); - list.add('b').tail(); - list.add('c').tail(); - - // "a" <-> "b" <-> "c" - - const index = list.indexOf('x'); - - expect(index).toBe(-1); - expect(list.length).toBe(3); - expect(list.head.value).toBe('a'); - expect(list.head.next.value).toBe('b'); - expect(list.head.next.next.value).toBe('c'); - expect(list.tail.value).toBe('c'); - expect(list.tail.previous.value).toBe('b'); - expect(list.tail.previous.previous.value).toBe('a'); - }); - }); - - describe('#toArray', () => { - it('should return array representation', () => { - list.addTail('a'); - list.addTail(2); - list.addTail('c'); - list.addTail({ k: 4, v: 'd' }); - - // "a" <-> 2 <-> "c" <-> {"k":4,"v":"d"} - - const arr = list.toArray(); - expect(arr).toEqual(['a', 2, 'c', { k: 4, v: 'd' }]); - }); - - it('should return empty array when list is empty', () => { - const arr = list.toArray(); - expect(arr).toEqual([]); - }); - }); - - describe('#toNodeArray', () => { - it('should return array of nodes', () => { - list.addTail('a'); - list.addTail(2); - list.addTail('c'); - list.addTail({ k: 4, v: 'd' }); - - // "a" <-> 2 <-> "c" <-> {"k":4,"v":"d"} - - const arr = list.toNodeArray(); - - expect(arr.every(node => node instanceof ListNode)).toBe(true); - expect(arr.map(node => node.value)).toEqual(['a', 2, 'c', { k: 4, v: 'd' }]); - }); - - it('should return empty array when list is empty', () => { - const arr = list.toNodeArray(); - expect(arr).toEqual([]); - }); - }); - - describe('#toString', () => { - it('should return string representation', () => { - list.addTail('a'); - list.addTail(2); - list.addTail('c'); - list.addTail({ k: 4, v: 'd' }); - - // "a" <-> 2 <-> "c" <-> {"k":4,"v":"d"} - - const str = list.toString(); - expect(str).toBe('"a" <-> 2 <-> "c" <-> {"k":4,"v":"d"}'); - }); - - it('should return empty string when list is empty', () => { - const str = list.toString(); - expect(str).toBe(''); - }); - - it('should be coercible', () => { - list.addMany(['a', 'b', 'c']).tail(); - - // "a" <-> "b" <-> "c" - - expect('' + list).toBe('"a" <-> "b" <-> "c"'); - }); - - it('should be able to receive a custom mapperFn', () => { - list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }]).tail(); - - // {"x":1} <-> {"x":2} <-> {"x":3} - - const str = list.toString(value => value.x); - expect(str).toBe('1 <-> 2 <-> 3'); - }); - }); - - it('should be iterable', () => { - list.addTail('a'); - list.addTail('b'); - list.addTail('c'); - - // "a" <-> "b" <-> "c" - - const arr = []; - - for (const value of list) { - arr.push(value); - } - - expect(arr).toEqual(['a', 'b', 'c']); - }); -}); - -interface X { - [k: string]: any; -} diff --git a/npm/ng-packs/packages/core/src/lib/utils/index.ts b/npm/ng-packs/packages/core/src/lib/utils/index.ts index a112d9acd8..0043152ada 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/index.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/index.ts @@ -1,6 +1,5 @@ export * from './common-utils'; export * from './generator-utils'; export * from './initial-utils'; -export * from './linked-list'; export * from './route-utils'; export * from './rxjs-utils'; diff --git a/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts b/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts deleted file mode 100644 index 9bd3e21941..0000000000 --- a/npm/ng-packs/packages/core/src/lib/utils/linked-list.ts +++ /dev/null @@ -1,396 +0,0 @@ -/* tslint:disable:no-non-null-assertion */ - -import compare from 'just-compare'; - -export class ListNode { - readonly value: T; - next: ListNode | undefined; - previous: ListNode | undefined; - - constructor(value: T) { - this.value = value; - } -} - -export class LinkedList { - private first: ListNode | undefined; - private last: ListNode | undefined; - private size = 0; - - get head(): ListNode | undefined { - return this.first; - } - get tail(): ListNode | undefined { - return this.last; - } - get length(): number { - return this.size; - } - - private attach( - value: T, - previousNode: ListNode | undefined, - nextNode: ListNode | undefined, - ): ListNode { - if (!previousNode) return this.addHead(value); - - if (!nextNode) return this.addTail(value); - - const node = new ListNode(value); - node.previous = previousNode; - previousNode.next = node; - node.next = nextNode; - nextNode.previous = node; - - this.size++; - - return node; - } - - private attachMany( - values: T[], - previousNode: ListNode | undefined, - nextNode: ListNode | undefined, - ): ListNode[] { - if (!values.length) return []; - - if (!previousNode) return this.addManyHead(values); - - if (!nextNode) return this.addManyTail(values); - - const list = new LinkedList(); - list.addManyTail(values); - list.first!.previous = previousNode; - previousNode.next = list.first; - list.last!.next = nextNode; - nextNode.previous = list.last; - - this.size += values.length; - - return list.toNodeArray(); - } - - private detach(node: ListNode) { - if (!node.previous) return this.dropHead(); - - if (!node.next) return this.dropTail(); - - node.previous.next = node.next; - node.next.previous = node.previous; - - this.size--; - - return node; - } - - add(value: T) { - return { - after: (previousValue: T, compareFn: ListComparisonFn = compare) => - this.addAfter(value, previousValue, compareFn), - before: (nextValue: T, compareFn: ListComparisonFn = compare) => - this.addBefore(value, nextValue, compareFn), - byIndex: (position: number) => this.addByIndex(value, position), - head: () => this.addHead(value), - tail: () => this.addTail(value), - }; - } - - addMany(values: T[]) { - return { - after: (previousValue: T, compareFn: ListComparisonFn = compare) => - this.addManyAfter(values, previousValue, compareFn), - before: (nextValue: T, compareFn: ListComparisonFn = compare) => - this.addManyBefore(values, nextValue, compareFn), - byIndex: (position: number) => this.addManyByIndex(values, position), - head: () => this.addManyHead(values), - tail: () => this.addManyTail(values), - }; - } - - addAfter(value: T, previousValue: T, compareFn: ListComparisonFn = compare): ListNode { - const previous = this.find(node => compareFn(node.value, previousValue)); - - return previous ? this.attach(value, previous, previous.next) : this.addTail(value); - } - - addBefore(value: T, nextValue: T, compareFn: ListComparisonFn = compare): ListNode { - const next = this.find(node => compareFn(node.value, nextValue)); - - return next ? this.attach(value, next.previous, next) : this.addHead(value); - } - - addByIndex(value: T, position: number): ListNode { - if (position < 0) position += this.size; - else if (position >= this.size) return this.addTail(value); - - if (position <= 0) return this.addHead(value); - - const next = this.get(position)!; - - return this.attach(value, next.previous, next); - } - - addHead(value: T): ListNode { - const node = new ListNode(value); - - node.next = this.first; - - if (this.first) this.first.previous = node; - else this.last = node; - - this.first = node; - this.size++; - - return node; - } - - addTail(value: T): ListNode { - const node = new ListNode(value); - - if (this.first) { - node.previous = this.last; - this.last!.next = node; - this.last = node; - } else { - this.first = node; - this.last = node; - } - - this.size++; - - return node; - } - - addManyAfter( - values: T[], - previousValue: T, - compareFn: ListComparisonFn = compare, - ): ListNode[] { - const previous = this.find(node => compareFn(node.value, previousValue)); - - return previous ? this.attachMany(values, previous, previous.next) : this.addManyTail(values); - } - - addManyBefore( - values: T[], - nextValue: T, - compareFn: ListComparisonFn = compare, - ): ListNode[] { - const next = this.find(node => compareFn(node.value, nextValue)); - - return next ? this.attachMany(values, next.previous, next) : this.addManyHead(values); - } - - addManyByIndex(values: T[], position: number): ListNode[] { - if (position < 0) position += this.size; - - if (position <= 0) return this.addManyHead(values); - - if (position >= this.size) return this.addManyTail(values); - - const next = this.get(position)!; - - return this.attachMany(values, next.previous, next); - } - - addManyHead(values: T[]): ListNode[] { - return values.reduceRight[]>((nodes, value) => { - nodes.unshift(this.addHead(value)); - return nodes; - }, []); - } - - addManyTail(values: T[]): ListNode[] { - return values.map(value => this.addTail(value)); - } - - drop() { - return { - byIndex: (position: number) => this.dropByIndex(position), - byValue: (value: T, compareFn: ListComparisonFn = compare) => - this.dropByValue(value, compareFn), - byValueAll: (value: T, compareFn: ListComparisonFn = compare) => - this.dropByValueAll(value, compareFn), - head: () => this.dropHead(), - tail: () => this.dropTail(), - }; - } - - dropMany(count: number) { - return { - byIndex: (position: number) => this.dropManyByIndex(count, position), - head: () => this.dropManyHead(count), - tail: () => this.dropManyTail(count), - }; - } - - dropByIndex(position: number): ListNode | undefined { - if (position < 0) position += this.size; - - const current = this.get(position); - - return current ? this.detach(current) : undefined; - } - - dropByValue(value: T, compareFn: ListComparisonFn = compare): ListNode | undefined { - const position = this.findIndex(node => compareFn(node.value, value)); - - return position < 0 ? undefined : this.dropByIndex(position); - } - - dropByValueAll(value: T, compareFn: ListComparisonFn = compare): ListNode[] { - const dropped: ListNode[] = []; - - for (let current = this.first, position = 0; current; position++, current = current.next) { - if (compareFn(current.value, value)) { - dropped.push(this.dropByIndex(position - dropped.length)!); - } - } - - return dropped; - } - - dropHead(): ListNode | undefined { - const head = this.first; - - if (head) { - this.first = head.next; - - if (this.first) this.first.previous = undefined; - else this.last = undefined; - - this.size--; - - return head; - } - - return undefined; - } - - dropTail(): ListNode | undefined { - const tail = this.last; - - if (tail) { - this.last = tail.previous; - - if (this.last) this.last.next = undefined; - else this.first = undefined; - - this.size--; - - return tail; - } - - return undefined; - } - - dropManyByIndex(count: number, position: number): ListNode[] { - if (count <= 0) return []; - - if (position < 0) position = Math.max(position + this.size, 0); - else if (position >= this.size) return []; - - count = Math.min(count, this.size - position); - - const dropped: ListNode[] = []; - - while (count--) { - const current = this.get(position); - dropped.push(this.detach(current!)!); - } - - return dropped; - } - - dropManyHead(count: Exclude): ListNode[] { - if (count <= 0) return []; - - count = Math.min(count, this.size); - - const dropped: ListNode[] = []; - - while (count--) dropped.unshift(this.dropHead()!); - - return dropped; - } - - dropManyTail(count: Exclude): ListNode[] { - if (count <= 0) return []; - - count = Math.min(count, this.size); - - const dropped: ListNode[] = []; - - while (count--) dropped.push(this.dropTail()!); - - return dropped; - } - - find(predicate: ListIteratorFn): ListNode | undefined { - for (let current = this.first, position = 0; current; position++, current = current.next) { - if (predicate(current, position, this)) return current; - } - - return undefined; - } - - findIndex(predicate: ListIteratorFn): number { - for (let current = this.first, position = 0; current; position++, current = current.next) { - if (predicate(current, position, this)) return position; - } - - return -1; - } - - forEach(callback: ListIteratorFn) { - for (let node = this.first, position = 0; node; position++, node = node.next) { - callback(node, position, this); - } - } - - get(position: number): ListNode | undefined { - return this.find((_, index) => position === index); - } - - indexOf(value: T, compareFn: ListComparisonFn = compare): number { - return this.findIndex(node => compareFn(node.value, value)); - } - - toArray(): T[] { - const array = new Array(this.size); - - this.forEach((node, index) => (array[index!] = node.value)); - - return array; - } - - toNodeArray(): ListNode[] { - const array = new Array(this.size); - - this.forEach((node, index) => (array[index!] = node)); - - return array; - } - - toString(mapperFn: ListMapperFn = JSON.stringify): string { - return this.toArray() - .map(value => mapperFn(value)) - .join(' <-> '); - } - - *[Symbol.iterator]() { - for (let node = this.first, position = 0; node; position++, node = node.next) { - yield node.value; - } - } -} - -export type ListMapperFn = (value: T) => any; - -export type ListComparisonFn = (value1: T, value2: T) => boolean; - -export type ListIteratorFn = ( - node: ListNode, - index?: number, - list?: LinkedList, -) => R;