mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
add Node.js environment setup
This commit is contained in:
parent
b80b3ed0f8
commit
4ea1ee0c04
7 changed files with 294 additions and 27 deletions
216
Flow.Launcher.Core/ExternalPlugins/PluginEnvironment.cs
Normal file
216
Flow.Launcher.Core/ExternalPlugins/PluginEnvironment.cs
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
using Droplex;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Flow.Launcher.Core.ExternalPlugins
|
||||
{
|
||||
internal class PluginEnvironment
|
||||
{
|
||||
private const string PythonExecutable = "pythonw.exe";
|
||||
|
||||
private const string NodeExecutable = "node.exe";
|
||||
|
||||
private const string PythonEnv = "Python";
|
||||
|
||||
private const string NodeEnv = "Node.js";
|
||||
|
||||
private List<PluginMetadata> pluginMetadataList;
|
||||
|
||||
private PluginsSettings pluginSettings;
|
||||
|
||||
internal PluginEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings)
|
||||
{
|
||||
this.pluginMetadataList = pluginMetadataList;
|
||||
this.pluginSettings = pluginSettings;
|
||||
}
|
||||
//TODO: CHECK IF NEED TO RESET PATH AFTER FLOW UPDATE
|
||||
// LOG NODE PATH
|
||||
internal IEnumerable<PluginPair> PythonSetup()
|
||||
{
|
||||
return Setup(AllowedLanguage.Python, PythonEnv);
|
||||
}
|
||||
|
||||
internal IEnumerable<PluginPair> TypeScriptSetup()
|
||||
{
|
||||
return Setup(AllowedLanguage.TypeScript, NodeEnv);
|
||||
}
|
||||
|
||||
internal IEnumerable<PluginPair> JavaScriptSetup()
|
||||
{
|
||||
return Setup(AllowedLanguage.JavaScript, NodeEnv);
|
||||
}
|
||||
|
||||
private IEnumerable<PluginPair> Setup(string languageType, string environment)
|
||||
{
|
||||
if (!pluginMetadataList.Any(o => o.Language.Equals(languageType, StringComparison.OrdinalIgnoreCase)))
|
||||
return new List<PluginPair>();
|
||||
|
||||
var envFilePath = string.Empty;
|
||||
|
||||
switch (languageType)
|
||||
{
|
||||
case AllowedLanguage.Python:
|
||||
if (!string.IsNullOrEmpty(pluginSettings.PythonDirectory) && FilesFolders.LocationExists(pluginSettings.PythonDirectory))
|
||||
return SetPathForPluginPairs($"{pluginSettings.PythonDirectory}\\{PythonExecutable}", languageType);
|
||||
break;
|
||||
|
||||
case AllowedLanguage.TypeScript:
|
||||
case AllowedLanguage.JavaScript:
|
||||
if (!string.IsNullOrEmpty(pluginSettings.NodeFilePath) && FilesFolders.FileExists(pluginSettings.NodeFilePath))
|
||||
return SetPathForPluginPairs(pluginSettings.NodeFilePath, languageType);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (MessageBox.Show($"Flow detected you have installed {languageType} plugins, which " +
|
||||
$"will require {environment} to run. Would you like to download {environment}? " +
|
||||
Environment.NewLine + Environment.NewLine +
|
||||
"Click no if it's already installed, " +
|
||||
$"and you will be prompted to select the folder that contains the {environment} executable",
|
||||
string.Empty, MessageBoxButtons.YesNo) == DialogResult.No)
|
||||
{
|
||||
var dlg = new OpenFileDialog
|
||||
{
|
||||
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
|
||||
Multiselect = false,
|
||||
CheckFileExists = true,
|
||||
CheckPathExists = true,
|
||||
Title = $"Please select the {environment} executable"
|
||||
};
|
||||
|
||||
var result = dlg.ShowDialog();
|
||||
if (result == DialogResult.OK)
|
||||
{
|
||||
switch (languageType)
|
||||
{
|
||||
case AllowedLanguage.Python:
|
||||
Constant.PythonPath = dlg.FileName;
|
||||
pluginSettings.PythonDirectory = Constant.PythonPath;
|
||||
break;
|
||||
case AllowedLanguage.TypeScript:
|
||||
case AllowedLanguage.JavaScript:
|
||||
Constant.NodePath = dlg.FileName;
|
||||
pluginSettings.NodeFilePath = Constant.NodePath;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
InstallEnvironment(languageType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
InstallEnvironment(languageType);
|
||||
}
|
||||
|
||||
switch (languageType)
|
||||
{
|
||||
case AllowedLanguage.Python when FilesFolders.FileExists(Constant.PythonPath) && !string.IsNullOrEmpty(pluginSettings.PythonDirectory):
|
||||
return SetPathForPluginPairs(Constant.PythonPath, languageType);
|
||||
|
||||
case AllowedLanguage.TypeScript when FilesFolders.FileExists(Constant.NodePath) && !string.IsNullOrEmpty(pluginSettings.NodeFilePath):
|
||||
case AllowedLanguage.JavaScript when FilesFolders.FileExists(Constant.NodePath) && !string.IsNullOrEmpty(pluginSettings.NodeFilePath):
|
||||
return SetPathForPluginPairs(pluginSettings.NodeFilePath, languageType);
|
||||
|
||||
default:
|
||||
MessageBox.Show(
|
||||
"Unable to set Python executable path, please try from Flow's settings (scroll down to the bottom).");
|
||||
Log.Error("PluginsLoader",
|
||||
$"Not able to successfully set Python path, the PythonDirectory variable is still an empty string.",
|
||||
"PythonPlugins");
|
||||
|
||||
return new List<PluginPair>();
|
||||
}
|
||||
}
|
||||
|
||||
private void InstallEnvironment(string languageType)
|
||||
{
|
||||
switch (languageType)
|
||||
{
|
||||
case AllowedLanguage.Python:
|
||||
var pythonDirPath = Path.Combine(DataLocation.DataDirectory(), "PythonEmbeddable");
|
||||
FilesFolders.RemoveFolderIfExists(pythonDirPath);
|
||||
|
||||
// Python 3.8.9 is used for Windows 7 compatibility
|
||||
DroplexPackage.Drop(App.python_3_8_9_embeddable, pythonDirPath).Wait();
|
||||
|
||||
pluginSettings.PythonDirectory = pythonDirPath;
|
||||
Constant.PythonPath = Path.Combine(pythonDirPath, PythonExecutable);
|
||||
|
||||
break;
|
||||
|
||||
case AllowedLanguage.TypeScript:
|
||||
case AllowedLanguage.JavaScript:
|
||||
var nodeDirPath = Path.Combine(DataLocation.DataDirectory(), "Node");
|
||||
FilesFolders.RemoveFolderIfExists(nodeDirPath);
|
||||
|
||||
DroplexPackage.Drop(App.nodejs_16_18_0, nodeDirPath).Wait();
|
||||
|
||||
Constant.NodePath = Path.Combine(nodeDirPath, $"node-v16.18.0-win-x64\\{NodeExecutable}");
|
||||
pluginSettings.NodeFilePath = Constant.NodePath;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<PluginPair> SetPathForPluginPairs(string filePath, string languageToSet)
|
||||
{
|
||||
var pluginPairs = new List<PluginPair>();
|
||||
|
||||
foreach (var metadata in pluginMetadataList) {
|
||||
|
||||
switch (languageToSet)
|
||||
{
|
||||
case AllowedLanguage.Python when metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase):
|
||||
pluginPairs.Add(new PluginPair
|
||||
{
|
||||
Plugin = new PythonPlugin(filePath),
|
||||
Metadata = metadata
|
||||
});
|
||||
break;
|
||||
|
||||
case AllowedLanguage.TypeScript when metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase):
|
||||
pluginPairs.Add(new PluginPair
|
||||
{
|
||||
Plugin = new NodePlugin(filePath),
|
||||
Metadata = metadata
|
||||
});
|
||||
break;
|
||||
|
||||
case AllowedLanguage.JavaScript when metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase):
|
||||
pluginPairs.Add(new PluginPair
|
||||
{
|
||||
Plugin = new NodePlugin(filePath),
|
||||
Metadata = metadata
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pluginPairs;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Droplex" Version="1.4.1" />
|
||||
<PackageReference Include="Droplex" Version="1.6.0" />
|
||||
<PackageReference Include="FSharp.Core" Version="6.0.6" />
|
||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.1.3" />
|
||||
<PackageReference Include="squirrel.windows" Version="1.5.2" NoWarn="NU1701" />
|
||||
|
|
|
|||
33
Flow.Launcher.Core/Plugin/NodePlugin.cs
Normal file
33
Flow.Launcher.Core/Plugin/NodePlugin.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Execution of JavaScript & TypeScript plugins
|
||||
/// </summary>
|
||||
internal class NodePlugin : JsonRPCPlugin
|
||||
{
|
||||
public override string SupportedLanguage { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
|
||||
public NodePlugin(string filename)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override string Request(JsonRPCRequestModel rpcRequest, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override Task<Stream> RequestAsync(JsonRPCRequestModel rpcRequest, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ using System.Reflection;
|
|||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Droplex;
|
||||
using Flow.Launcher.Core.ExternalPlugins;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
|
|
@ -22,9 +23,20 @@ namespace Flow.Launcher.Core.Plugin
|
|||
public static List<PluginPair> Plugins(List<PluginMetadata> metadatas, PluginsSettings settings)
|
||||
{
|
||||
var dotnetPlugins = DotNetPlugins(metadatas);
|
||||
var pythonPlugins = PythonPlugins(metadatas, settings);
|
||||
|
||||
var pluginEnv = new PluginEnvironment(metadatas, settings);
|
||||
var pythonPlugins = pluginEnv.PythonSetup();
|
||||
var tsPlugins = pluginEnv.TypeScriptSetup();
|
||||
var jsthonPlugins = pluginEnv.JavaScriptSetup();
|
||||
|
||||
var executablePlugins = ExecutablePlugins(metadatas);
|
||||
var plugins = dotnetPlugins.Concat(pythonPlugins).Concat(executablePlugins).ToList();
|
||||
|
||||
var plugins = dotnetPlugins
|
||||
.Concat(pythonPlugins)
|
||||
.Concat(tsPlugins)
|
||||
.Concat(jsthonPlugins)
|
||||
.Concat(executablePlugins)
|
||||
.ToList();
|
||||
return plugins;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
|
|
@ -30,6 +30,7 @@ namespace Flow.Launcher.Infrastructure
|
|||
public static readonly string MissingImgIcon = Path.Combine(ImagesDirectory, "app_missing_img.png");
|
||||
|
||||
public static string PythonPath;
|
||||
public static string NodePath;
|
||||
|
||||
public static readonly string QueryTextBoxIconImagePath = $"{ProgramDirectory}\\Images\\mainsearch.svg";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.UserSettings
|
||||
{
|
||||
public class PluginsSettings : BaseModel
|
||||
{
|
||||
public string PythonDirectory { get; set; }
|
||||
public string PythonDirectory { get; set; } // IS FILE PATH BUT CHANGE NAME TO FILE PATH??
|
||||
|
||||
public string NodeFilePath { get; set; }
|
||||
|
||||
public Dictionary<string, Plugin> Plugins { get; set; } = new Dictionary<string, Plugin>();
|
||||
|
||||
public void UpdatePluginSettings(List<PluginMetadata> metadatas)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
namespace Flow.Launcher.Plugin
|
||||
using System;
|
||||
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Allowed plugin languages
|
||||
|
|
@ -8,34 +10,32 @@
|
|||
/// <summary>
|
||||
/// Python
|
||||
/// </summary>
|
||||
public static string Python
|
||||
{
|
||||
get { return "PYTHON"; }
|
||||
}
|
||||
public const string Python = "Python";
|
||||
|
||||
/// <summary>
|
||||
/// C#
|
||||
/// </summary>
|
||||
public static string CSharp
|
||||
{
|
||||
get { return "CSHARP"; }
|
||||
}
|
||||
public const string CSharp = "CSharp";
|
||||
|
||||
/// <summary>
|
||||
/// F#
|
||||
/// </summary>
|
||||
public static string FSharp
|
||||
{
|
||||
get { return "FSHARP"; }
|
||||
}
|
||||
public const string FSharp = "FSharp";
|
||||
|
||||
/// <summary>
|
||||
/// Standard .exe
|
||||
/// </summary>
|
||||
public static string Executable
|
||||
{
|
||||
get { return "EXECUTABLE"; }
|
||||
}
|
||||
public const string Executable = "Executable";
|
||||
|
||||
/// <summary>
|
||||
/// TypeScript
|
||||
/// </summary>
|
||||
public const string TypeScript = "TypeScript";
|
||||
|
||||
/// <summary>
|
||||
/// JavaScript
|
||||
/// </summary>
|
||||
public const string JavaScript = "JavaScript";
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this language is a .NET language
|
||||
|
|
@ -44,8 +44,8 @@
|
|||
/// <returns></returns>
|
||||
public static bool IsDotNet(string language)
|
||||
{
|
||||
return language.ToUpper() == CSharp
|
||||
|| language.ToUpper() == FSharp;
|
||||
return language.Equals(CSharp, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(FSharp, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -56,8 +56,10 @@
|
|||
public static bool IsAllowed(string language)
|
||||
{
|
||||
return IsDotNet(language)
|
||||
|| language.ToUpper() == Python.ToUpper()
|
||||
|| language.ToUpper() == Executable.ToUpper();
|
||||
|| language.Equals(Python, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(Executable, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(TypeScript, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(JavaScript, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue