Sunday, 20 December 2020

Outputting runnable SQL from Entity Framework 6.x

This article will describe how you can output runnable SQL from Entity Framework. The output will be sent to the Console and Debug. You can easily modify this to output to other output sources, such as tracing or files for that matter. What is important is that we need to interpolate the parameters from Entity Framework so that we get a runnable SQL. Entity Framework parameterizes the SQL queries such that SQL injection is avoided. Where conditions and similar are inserted into parameters, notably with the p__linq naming convention. We will interpolate these parameters into runnable SQL such that you can paste SQL into SQL Server Management Studio (SMSMS). Or you could save the runnable SQL to a .sql file and let SQLCMD run it from command line. Either way, we must set up the DbContext to do this. I am using Entity Framework 6.2.0. It should be possible to use this technique with all EF 6.x version. In Entity Framework Core and Entity Framework Core 2, the techniques will be similar. First define a DbConfiguration and attribute the DbContext class you are using like this with the DbConfigurationType (we are not considering ObjectContext in this article, but DbContext is a wrapper around this class anyways, so you should be apply to techniques taught here to other scenarios).
SomeAcmeDbContext.cs
namespace SomeAcme.Data.EntityFramework { [DbConfigurationType(typeof(SomeAcmeDataContextConfiguration))] public partial class SomeAcmeDataContext : System.Data.Entity.DbContext, ISomeAcmeDataContext { ..
Ok, so our DbConfiguration just inherits from DbConfiguration and sets up a custom DatabaseLogFormatter like this:
  
SomeAcmeDataContextConfiguration.cs
using System.Data.Entity; namespace SomeAcme.Data.EntityFramework.DbContext { public class SomeAcmeDataContextConfiguration : DbConfiguration { public SomeAcmeDataContextConfiguration() { SetDatabaseLogFormatter((context, logAction) => new SomeAcmeDbLogFormatter(context, logAction)); } } }
SetDatabaseLogFormatter is a protected method o DbConfiguration. Our DatabaseLogFormatter implementation then looks like this:
 
SomeAcmeDbLogFormatter.cs
using System; using System.Data.Common; using System.Data.Entity.Infrastructure.Interception; using SomeAcme.Data.EntityFramework.DbContext.Extensions; namespace SomeAcme.Data.EntityFramework.DbContext { public class SomeAcmeDbLogFormatter : DatabaseLogFormatter { public SomeAcmeDbLogFormatter(System.Data.Entity.DbContext dbContext, Action<string> loggingAction) : base(dbContext, loggingAction) { } public override void LogCommand<TResult>(DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext) { string cmdText = command.CommandText; if (string.IsNullOrEmpty(cmdText)) return; if (cmdText.StartsWith("Openend connection", StringComparison.InvariantCultureIgnoreCase) || cmdText.StartsWith("Closed connection", StringComparison.InvariantCultureIgnoreCase)) return; Write($"--DbContext {Context.GetType().Name} is executing command against DB {Context.Database.Connection.Database}: {Environment.NewLine}{command.GetGeneratedQuery().Replace(Environment.NewLine, "")} {Environment.NewLine}"); } public override void LogResult<TResult>(DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext) { //empty by intention } } }
We also have a helper extension method called GetGeneratedQuery on DbCommand objects to help us get the cruft of this article - the interpolated runnable query. From my testing we can just interpolate the parameters as is in most use cases. However, some datatypes in the T-SQL world must be quoted (like, strings) and we need to adjust the date and time data types to a runnable format too. In case you find this helper method should be interpolated, please let me know. Our helper method GetGeneratedQuery looks like this:
  
SomeAcmeDbCommandExtensions.cs
using System; using System.Data; using System.Data.Common; using System.Data.SqlClient; using System.Linq; using System.Text; namespace SomeAcme.Data.EntityFramework.DbContext.Extensions { public static class DbCommandExtensions { /// <summary> /// Returns the generated sql string where parameters are replaced by value. Generated a runnable /// SQL script. Note that this is an approximation anwyays, but gives us a runnable query. The database server query engine optimizer will possible rewrite /// even simple queries if it sees it possible to rearrange the query to predictively create a more efficient query. /// </summary> /// <param name="dbCommand"></param> /// <returns></returns> public static string GetGeneratedQuery(this DbCommand dbCommand) { DbType[] quotedParameterTypes = new DbType[] { DbType.AnsiString, DbType.Date, DbType.DateTime, DbType.DateTime2, DbType.Guid, DbType.String, DbType.AnsiStringFixedLength, DbType.StringFixedLength }; var sb = new StringBuilder(); sb.AppendLine(dbCommand.CommandText); var arrParams = new SqlParameter[dbCommand.Parameters.Count]; dbCommand.Parameters.CopyTo(arrParams, 0); //copy dbCommand parameters into another collection to avoid //mutating the query and be able to run a foreach loop foreach (SqlParameter p in arrParams.OrderByDescending(p => p.ParameterName.Length)) { string value = p.Value.ToString(); if (p.DbType == DbType.Date || p.DbType == DbType.DateTime || p.DbType == DbType.DateTime2) { value = DateTime.Parse(value).ToString("yyyy-MM-dd HH:mm:ss.fff"); } if (quotedParameterTypes.Contains(p.DbType)) value = "'" + value + "'"; sb.Replace("@" + p.ParameterName, value); } return sb.ToString(); } } }
We also need to activate database logging in the first place. Database logging to the console and debug should be avoided in production in ordinary cases, as they make a performance impact. Instead, it is handy to turn it on or off via an app setting. I have decided to only allow it while debugging so my constructors of my DbContext where I have tested it calls this method:
SomeAcmeDbContext.cs
(once more need to add some code) private void SetupDbContextBehavior() { Configuration.AutoDetectChangesEnabled = true; Configuration.LazyLoadingEnabled = true; ObjectContext.CommandTimeout = 10 * 60; #if DEBUG //To enable outputting database traffic to the console, set the app setting OutputDatabaseTrafficLogging in web.config to true //this must not be activated in production. To safe guard this, //this block below is wrapped in the debug preprocessor directive. bool outputDatabaseTrafficLogging = ConfigurationManagerWrapper.GetAppsetting(SomeAcme.Common.Constants.OutputDatabaseTrafficLogging); if (outputDatabaseTrafficLogging) { Database.Log = s => { if (s.StartsWith("Opened connection", StringComparison.InvariantCultureIgnoreCase) || s.StartsWith("Closed connection", StringComparison.InvariantCultureIgnoreCase)) return; Console.WriteLine(s); Debug.WriteLine(s); }; } #endif
Never mind the first three lines, they are just added here as tips for additional settings you CAN set if you want to. The important bit is the Database.Log delegate property, which acceps a lambda for example where you set up what to do with the logging. Here we just tell the DbContext that if the app setting OutputDatabaseTrafficLogging is set to true, we output the runnable SQL from Entity Framework to the console. That's all there is to it! You can now activate the app setting and see in the debug output (or in console) runnable SQL. And you can paste the SQL into SMSS for example to check for performance issues such as missing indexes and similar or tune up the size of the result sets and alter the SQL. You should also consider making your DbContext runnable in Linqpad for easier tuning of EF queries, but that is for another article. Happy coding!

Saturday, 19 December 2020

Running Angular on a fixed port on Windows platform

This article is for Angular developers running on a Windows platform. We will use Windows domain tools such as netstat and Powershell in this article. A cross platform version of Powershell exist. I have not tested this approach on other OS-es, such as Linux. Linux developers using Angular (if any) might follow my approach here with success, it is is possible to do this in a similar manner on *nix systems. Also note that this article is meant for those using Angular SPA with .Net Core. This is the standard setup for Windows platform and Angular development. When developing an Angular app locally, sometimes you want to run on a fixed port. For example, your app might federate its access towards ADFS (Active Directory Federated Services) and you desire to have a fixed port. This makes it possible to set up a callback url that can be fixed and not have the standard setup with a random port that Angular and webpack sets up for you. Here is how I managed to achieve to override this and set up a fixed port. First off, we need to create a short Powershell script with a function to stop (kill) the processes running at a given port with the following contents:


KillAngularApp.ps1
param ( [Parameter(Mandatory=$true)][string] $portToFind ) # This PS script requires the paramter $portToFind to be passed into it pwd Write-Host Probing for Angular App running at $portToFind $runningEudAppProcessLocal = 'netstat -ano | findstr "$portToFind"' $arr = Invoke-Expression $runningEudAppProcessLocal # $runningEudAppProcessLocal $arr = $arr -split '\s+' Write-Host Probing complete: $arr if ($arr.Length -ge 5) { $runningAngularAppPort = $arr[5] $runningAngularAppPort Write-Host Killing the process.. $killScript = "taskkill /PID $runningAngularAppPort /F" Invoke-Expression $killScript Write-Host probing once more $arr = Invoke-Expression $runningEudAppProcessLocal if ($arr.Length -eq 0){ Write-Host There is no running process any more at $portToFind } }
The powershell script above runs 'netstat ano -findstr "someportnumber"'. It finds the PID at that port and splits the result using the '\s+' expression, i.e. whitespace. If we find a PID (process id), we stop that process using the 'taskkill' command with the '-force' flag. We then need to have a C# class that .Net Core can call. The code here has been tested with .Net Core 3.0 and 3.1 successfully.


KillAngularPortHelper.cs
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using System; using System.Diagnostics; namespace EndUserDevice { public static class AngularKillPortHelper { /// <summary> /// Kills Angular app running with for example node at configured port Host:SpaPort /// </summary> public static void KillPort() { try { string environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); string configFile = environmentName == Environments.Development ? "appsettings.json" : environmentName == Environments.Staging ? "appsettings.Staging.json" : environmentName == Environments.Production ? "appsettings.Production.json" : "appsettings.json"; // Set up configuration sources. var config = new ConfigurationBuilder() .AddJsonFile(configFile, optional: false) .Build(); string angularAppPort = config.GetValue<string>("Configuration:Host:SpaPort"); if (environmentName == Environments.Development) { string killAngularAppRunningAtPortMessage = $"Trying to shutdown existing running Angular SPA if it is running at reserved fixed port: {angularAppPort}"; Debug.WriteLine(killAngularAppRunningAtPortMessage); Console.WriteLine(killAngularAppRunningAtPortMessage); //requires Nuget Packages: //Microsoft.Powershell.SDK //System.Management.Automation string ps1File = config.GetValue<string>("Configuration:Host:CloseSpaPortScriptPath"); var startInfo = new ProcessStartInfo(); startInfo.FileName = "powershell.exe"; startInfo.Arguments = "-noprofile \"& \"\"" + ps1File + "\"\"\""; startInfo.Arguments += " -portToFind " + angularAppPort; startInfo.UseShellExecute = false; //WARNING!!! If the powershell script outputs lots of data, this code could hang //You will need to output using a stream reader and purge the contents from time to time startInfo.RedirectStandardOutput = !startInfo.UseShellExecute; startInfo.RedirectStandardError = !startInfo.UseShellExecute; //startInfo.CreateNoWindow = true; var process = new System.Diagnostics.Process(); process.StartInfo = startInfo; process.Start(); process.WaitForExit(3*1000); //if you want to limit how long you wait for the program to complete //input is in milliseconds //var seconds_to_wait_for_exit = 120; //process.WaitForExit(seconds_to_wait_for_exit * 1000); string output = ""; if (startInfo.RedirectStandardOutput) { output += "Standard Output"; output += Environment.NewLine; output += process.StandardOutput.ReadToEnd(); } if (startInfo.RedirectStandardError) { var error = process.StandardError.ReadToEnd(); if (!string.IsNullOrWhiteSpace(error)) { if (!string.IsNullOrWhiteSpace(output)) { output += Environment.NewLine; output += Environment.NewLine; } output += "Error Output"; output += Environment.NewLine; output += error; } } Console.WriteLine(output); Debug.WriteLine(output); } } catch (Exception err) { Console.WriteLine(err); } } } }
Finally we can configure our app to use the ports we want to use like this:

appsettings.json
"Configuration": { "Host": { "SpaPort": "44394", "ApiPort": "44364", "CloseSpaPortScriptPath": "c:\\dev\\someapp\\somesubdir\\KillAngularApp.ps1" }, }
The ApiPort here is not used by our code in our article. The SpaPort however is used. We then call this helper class like this in Program.cs of our asp.net core application hosting the Angular Spa:

Program.cs
public class Program { public static void Main(string[] args) { AngularKillPortHelper.KillPort(); IWebHost host = CreateWebHostBuilder(args).Build(); host.Run(); } //..
This makes sure that the port is ready and not buy by another app (such as your previous debugging session of the app!) and by killing the app running with node at that port, we make
sure that Angular can run at a fixed port. Are we done yet? No! We must also update the package.json file to use that port we want to use! This must correspond to the port we configure in the appsettings.json file to kill in the first place. (i.e. make sure the port is freely available and not busy)

package.json
{ "name": "enduserdevice", "version": "0.0.0", "scripts": { "ng": "ng", "start": "ng serve -o --ssl true --ssl-key /node_modules/browser-sync/certs/server.key --ssl-cert /node_modules/browser-sync/certs/server.crt --port=44394 &REM", "build": "ng build --base-href /eud/ --source-map --prod", "build-staging": "ng build --base-href /eud/ --source-map --staging", "build-mobile": "ng build --base-href /eud2/ --source-map -c mobile", "prod-build-dev": "ng build --prod --source-map --base-href /eud/ --prod", "build:ssr": "ng run EndUserDevice:server:dev", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" },
Never mind much of the setup above, the important bit to make note of here is the part: --port=44394 &REM The adjustments of the ng start script makes sure we use a fixed port of our Angular app. Sadly, as you can see - this is not easily configurable as we hard code it into our package.json. Maybe we could set this up as an environment variable in a Angular environment file.. ? Hope you found this article interesting. Having a fixed port is always handy when developing locally Angular app and you do no not like this random port thingy that is default set up otherwise. Works on my PC!

Saturday, 10 October 2020

Eslint Standalone in Azure Devops Build task

I have created a standalone tool that can run Eslint from the commandline. The tool is a Node.js application build with Pkg as a node10-win application, built as a standalone EXE file executable. You can find the repository here:

https://github.com/toreaurstadboss/eslint-standalone

Here you can also alter the application to your needs, if necessary. The application is available as a Npm package or a Nuget package on the official repos (npmjs.org and nuget.org) This article will focus on the use of the application via Nuget and activating the tool in Azure devops. First off make sure you add the official Nuget repo to your Nuget.config file like this:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageRestore>
    <add key="enabled" value="True" />
  </packageRestore>
  <activePackageSource>
    <!-- some other nuget repo in addition if desired -->
  </activePackageSource>
  <packageSources>
    <clear />
    <!-- some other nuget repo in addition if desired -->
    <add key="Nuget official repo" value="https://nuget.org/api/v2/" />
  </packageSources>
</configuration>

Now you can add a packagereference to the EslintStandalone.Cli tool in the .csproj project file (or .vbproj if you use Visual Basic) like this:

<PackageReference Include="EslintStandalone.Cli" Version="1.1.0" GeneratePathProperty="true" />
Also add the following copy step to copy the standalone.exe tool within the Nuget package out to the bin folder of your project: <ItemGroup> <Content Include="$(PkgEslintStandalone_Cli)\eslint-standalone.exe"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> </ItemGroup> This is possible since you use GeneratePathProperty set to true and we refer the folder of the nuget on disk like $(PkgEslintStandalone_Cli). The Nuget pakage is called EsLintStandalone.Cli. We replace '.' with '_' and we prefix always the variable to point to the nuget folder with Pkg and we reference the entire package with the $() expression. The next step is to add the execution of the tool in Azure Devops like a task. You can either define a single task or a Task group. I like task groups, since we then can easily share task among projects. The following command should be added:
dir
echo Starting Eslint tool to analyzing for compability issues in Javascript files
cd Source\SomeProject\bin
echo Current folder
dir *.exe
move eslint-standalone.exe ..
cd ..
echo Navigated to root folder of SomeProject. Starting the eslint-standalone tool. 
eslint-standalone.exe

Here we copy the standalone tool a level down to the root of the project, parent folder of bin folder. Here we usually have our target files, which will be Javascript files in our project with such files (e.g. a MVC project or other web projects usually). Finally we must supply a .eslintrc.js file, a config file for Eslint. At my work, I have customers that uses Internet Explorer 11. So I check for Ecmascript 5 compability. This tool can handle such as scenario. The following .eslintrc.js such suffice:

module.exports = {
    "plugins": ["ie11"],
    "env": {
      "browser": true,
      "node": true,
      "es6": false
    },
    "parserOptions": {
      "ecmaVersion": 5,
    },
    "rules": {
      "ie11/no-collection-args": ["error"],
      "ie11/no-for-in-const": ["error"],
      //"ie11/no-loop-func": ["warn"],
      "ie11/no-weak-collections": ["error"],
      "curly": ["off"]
    }
};

//A list of rules that can be applied is here: https://eslint.org/docs/rules/
//The rules can have the following severity in EsLint: "warn", "error" and "off".


https://eslint.org/docs/rules/ You can find Eslint rules at the link above. You can set the error level to either 'warn' or' 'error' or 'off'. https://eslint.org/docs/user-guide/configuring If you want to use the tool in a Npm based project, you can see the Npm page here: https://www.npmjs.com/package/eslint-standalone npm i eslint-standalone I got two version of the tool. Version 1.1. is recommended, as you must supply a .eslintrc.js file and have control over how the linting is done. Version 1.2. supplies a .eslintrc.js in the same folder as the tool with ES5 support detection as shown above included (.eslintrc.js file is bundled together). The tool itself is quite simple code in Node.js:

#!/usr/bin/env node

const CLIEngine = require("eslint").CLIEngine;
const minimist = require("minimist");
const path = require("path");
const chalk = require("chalk");
const eslintPluginCompat = require("eslint-plugin-compat");
const eslintIe11 = require("eslint-plugin-ie11");
const fs = require("fs");
const { promisify } = require("util");

const fsAccessAsync = promisify(fs.access);

var runEsLint = function(baseConfig, args) {
  const cli = new CLIEngine({ baseConfig });

  let filesDir = [];

  if (args.dir) {
    // Dir can be a string or an array, we do a preprocessing to always have an array
    filesDir = []
      .concat(args.dir)
      .map((item) => path.resolve(process.cwd(), item));
  } else {
    filesDir = ["./."];
  }

  console.log(`> eslint is checking the following dir: ${filesDir}`);

  const report = cli.executeOnFiles(filesDir);

  if (report.errorCount > 0) {
    const formatter = cli.getFormatter();

    console.log(
      chalk.bold.redBright(`> eslint has found ${report.errorCount} error(s)`)
    );
    console.log(formatter(report.results));

    process.exitCode = 1; //eslint errors encountered means the process should exit not with exit code 0.

    return;
  }
  console.log(chalk.bold.greenBright("> eslint finished without any errors!"));
  process.env.exitCode = 0; //exit with success code

}

var tryLoadConfigViaKnownSystemFolder = function(){

  let configFileFound = null;
try {
  let knownHomeDirectoryOnOSes =
    process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
  let knownHomeDirectoryOnOSesNormalized = path.normalize(
    knownHomeDirectoryOnOSes + "/.eslintrc"
  );
  configPath = path.resolve(knownHomeDirectoryOnOSesNormalized);
  if (checkIfFileExistsAndIsAccessible(configPath)){
    configFileFound = true;
    errorEncountered = false;
  }

} catch (error) {
  errorEncountered = true;
  console.error(error);  
  process.exitCode = 1; //signal an error has occured. https://stackoverflow.com/questions/5266152/how-to-exit-in-node-js
  return configFileFound;
}

};


var checkIfFileExistsAndIsAccessible = function(configPathFull) {
  try {
   fs.accessSync(configPathFull, fs.F_OK);
  return true;
  }
  catch (Error){
    return false;
   }  
}


var tryLoadFileInDirectoryStructure = function(curDir){

  let configFullPathFound = null;
  for (let i = 0; i < 100; i++) {
    try {
      if (i > 0) {
        console.info("Trying lib folder of eslint-standalone: " + curDir);
        let oldCurDir = curDir;
        curDir = path.resolve(curDir, ".."); //parent folder
        if (oldCurDir == curDir) {
          //at the top of media disk volume - exit for loop trying to retrieve the .eslintrc.js file from parent folder
          console.info(
            "It is recommended to save an .eslintrc.js file in the folder structure where you run this tool."
          );
          break;
        }
      }
      configPath = path.join(curDir + "/.eslintrc.js");
      configPath = path.normalize(configPath);
      if (checkIfFileExistsAndIsAccessible(configPath)){
       baseConfig = require(configPath);
       errorEncountered = false;
       configFullPathFound = configPath;
       break; //exit the for loop
      }
    } catch (error) {
      process.stdout.write(".");
      errorEncountered = true;
    }
  }
  return configFullPathFound;
}

var inspectArgs = function(args) {
  let fix = false;

  console.log("Looking at provided arguments:");
  for (var i = 0; i < args.length; i++) {
    console.log(args[i]);
    if (args[i] === "--fix") {
      fix = true;
      console.log("Fix option provided: " + fix);
      console.warn("Fix is not supported yet, you must manually adjust the files."
      );
    }
  }
}


module.exports = (() => {
  const args = process.argv.slice(2);

  inspectArgs(args); 

  // Read a default eslint config
  //console.log("Dirname: " + __dirname);

  let configPath = "";
  let baseConfig = "";
  let errorEncountered = false;

  console.info("Trying to resolve .eslintrc.js file");

  console.info("Trying current working directory:", process.cwd());

  let curDir = process.cwd();

  let configFilefound = tryLoadFileInDirectoryStructure(curDir);
  
  if (configFilefound === null) {
   curDir = __dirname;
   configFilefound = tryLoadFileInDirectoryStructure(curDir);
  }

  // try {
  //   configPath = path.join(curDir + "/.eslintrc.js");
  //   configPath = path.normalize(configPath);
  //   baseConfig = require(configPath);

  //   console.info("Found config file in current working folder");

  //   errorEncountered = false;
  //   configFilefound = baseConfig !== "";
  // } catch (error) {
  //   //ignore error handling for now at working folder
  //   configFilefound = false;
  // }

  // if (!configFilefound) {
  //   curDir = __dirname;

  //   for (let i = 0; i < 100; i++) {
  //     try {
  //       if (i > 0) {
  //         console.info("Trying lib folder of eslint-standalone: " + curDir);
  //         let oldCurDir = curDir;
  //         curDir = path.resolve(curDir, ".."); //parent folder
  //         if (oldCurDir == curDir) {
  //           //at the top of media disk volume - exit for loop trying to retrieve the .eslintrc.js file from parent folder
  //           console.info(
  //             "It is recommended to save an .eslintrc.js file in the folder structure where you run this tool."
  //           );
  //           break;
  //         }
  //       }
  //       configPath = path.join(curDir + "/.eslintrc.js");
  //       configPath = path.normalize(configPath);
  //       baseConfig = require(configPath);
  //       errorEncountered = false;
  //       break; //exit the for loop
  //     } catch (error) {
  //       process.stdout.write(".");
  //       errorEncountered = true;
  //     }
  //   }
  // }

  // Check if the path to a client config was specified
  if (args.conf) {
    if (Array.isArray(args.conf)) {
      const error = chalk.bold.redBright(
        `> eslint requires a single config file`
      );
      errorEncountered = true;
      console.warn(error);
    }

    try {
      configPath = path.resolve(process.cwd(), args.conf);
      baseConfig = require(configPath);
      errorEncountered = false;
    } catch (error) {
      errorEncountered = true;
      console.log(error);
    }
  }

  if (errorEncountered === true) {
    configFileFound = tryLoadConfigViaKnownSystemFolder();
    if (configFileFound !== null) {
      baseConfig = `{
        "extends": "${configPath}"         
      }`;    
    }
    // try {
    //   let knownHomeDirectoryOnOSes =
    //     process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
    //   let knownHomeDirectoryOnOSesNormalized = path.normalize(
    //     knownHomeDirectoryOnOSes + "/.eslintrc"
    //   );
    //   configPath = path.resolve(knownHomeDirectoryOnOSesNormalized);
    

    //   errorEncountered = false;
    // } catch (error) {
    //   errorEncountered = true;
    //   console.error(error);
    //   process.exitCode = 1; //signal an error has occured. https://stackoverflow.com/questions/5266152/how-to-exit-in-node-js
    //   return;
    // }
  }

  console.log(`> eslint has loaded config from: ${configFilefound}`);

  runEsLint(baseConfig, args);

  // console.log('base config: ');
  // console.log(baseConfig);

  // const cli = new CLIEngine({ baseConfig });

  // let filesDir = [];

  // if (args.dir) {
  //   // Dir can be a string or an array, we do a preprocessing to always have an array
  //   filesDir = []
  //     .concat(args.dir)
  //     .map((item) => path.resolve(process.cwd(), item));
  // } else {
  //   filesDir = ["./."];
  // }

  // console.log(`> eslint is checking the following dir: ${filesDir}`);

  // const report = cli.executeOnFiles(filesDir);

  // if (report.errorCount > 0) {
  //   const formatter = cli.getFormatter();

  //   console.log(
  //     chalk.bold.redBright(`> eslint has found ${report.errorCount} error(s)`)
  //   );
  //   console.log(formatter(report.results));

  //   process.exitCode = 1; //eslint errors encountered means the process should exit not with exit code 0.

  //   return;
  // }
  // console.log(chalk.bold.greenBright("> eslint finished without any errors!"));
  // process.env.exitCode = 0; //exit with success code
})();