diff --git a/build/common.ps1 b/build/common.ps1
index 172e22ad6a..16a6747d0f 100644
--- a/build/common.ps1
+++ b/build/common.ps1
@@ -7,6 +7,7 @@ $rootFolder = (Get-Item -Path "./" -Verbose).FullName
# List of solutions used only in development mode
$solutionPaths = @(
"../framework",
+ "../studio",
"../modules/basic-theme",
"../modules/users",
"../modules/permission-management",
diff --git a/modules/identity/src/Volo.Abp.Identity.Installer/Volo.Abp.Identity.Installer.csproj b/modules/identity/src/Volo.Abp.Identity.Installer/Volo.Abp.Identity.Installer.csproj
index 49fa7c35bf..80a1b51bb0 100644
--- a/modules/identity/src/Volo.Abp.Identity.Installer/Volo.Abp.Identity.Installer.csproj
+++ b/modules/identity/src/Volo.Abp.Identity.Installer/Volo.Abp.Identity.Installer.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/nupkg/common.ps1 b/nupkg/common.ps1
index a96de2bb00..8043c02306 100644
--- a/nupkg/common.ps1
+++ b/nupkg/common.ps1
@@ -336,4 +336,11 @@ $projects = (
"modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client",
"modules/cms-kit/src/Volo.CmsKit.Public.Web",
"modules/cms-kit/src/Volo.CmsKit.Web"
+
+ # abp/studio
+ "studio/src/Volo.Abp.Studio.Analyzing.Abstractions",
+ "studio/src/Volo.Abp.Studio.Domain.CommonServices",
+ "studio/src/Volo.Abp.Studio.Domain.Shared",
+ "studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions",
+ "studio/src/Volo.Abp.Studio.ModuleInstaller"
)
diff --git a/studio/Volo.Abp.Studio.Open.sln b/studio/Volo.Abp.Studio.Open.sln
new file mode 100644
index 0000000000..563e1d4602
--- /dev/null
+++ b/studio/Volo.Abp.Studio.Open.sln
@@ -0,0 +1,49 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{344A1096-CC05-42D1-BDDB-E254C27508A9}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Studio.Analyzing.Abstractions", "src\Volo.Abp.Studio.Analyzing.Abstractions\Volo.Abp.Studio.Analyzing.Abstractions.csproj", "{2271296E-33FD-46BE-97FD-9938193CB7D1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Studio.Domain.CommonServices", "src\Volo.Abp.Studio.Domain.CommonServices\Volo.Abp.Studio.Domain.CommonServices.csproj", "{1C86021F-E522-41A1-AC3F-01B0E474D75E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Studio.Domain.Shared", "src\Volo.Abp.Studio.Domain.Shared\Volo.Abp.Studio.Domain.Shared.csproj", "{28132E4E-FFB9-45C4-99AD-1BCA04B3E597}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Studio.ModuleInstaller", "src\Volo.Abp.Studio.ModuleInstaller\Volo.Abp.Studio.ModuleInstaller.csproj", "{4FA11334-D727-4C14-9D71-1DB46AC3BE9C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Studio.ModuleInstaller.Abstractions", "src\Volo.Abp.Studio.ModuleInstaller.Abstractions\Volo.Abp.Studio.ModuleInstaller.Abstractions.csproj", "{99679BF9-B888-4814-8E85-F751984CAFF8}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2271296E-33FD-46BE-97FD-9938193CB7D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2271296E-33FD-46BE-97FD-9938193CB7D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2271296E-33FD-46BE-97FD-9938193CB7D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2271296E-33FD-46BE-97FD-9938193CB7D1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1C86021F-E522-41A1-AC3F-01B0E474D75E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1C86021F-E522-41A1-AC3F-01B0E474D75E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1C86021F-E522-41A1-AC3F-01B0E474D75E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1C86021F-E522-41A1-AC3F-01B0E474D75E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {28132E4E-FFB9-45C4-99AD-1BCA04B3E597}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {28132E4E-FFB9-45C4-99AD-1BCA04B3E597}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {28132E4E-FFB9-45C4-99AD-1BCA04B3E597}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {28132E4E-FFB9-45C4-99AD-1BCA04B3E597}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4FA11334-D727-4C14-9D71-1DB46AC3BE9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4FA11334-D727-4C14-9D71-1DB46AC3BE9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4FA11334-D727-4C14-9D71-1DB46AC3BE9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4FA11334-D727-4C14-9D71-1DB46AC3BE9C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {99679BF9-B888-4814-8E85-F751984CAFF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {99679BF9-B888-4814-8E85-F751984CAFF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {99679BF9-B888-4814-8E85-F751984CAFF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {99679BF9-B888-4814-8E85-F751984CAFF8}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {2271296E-33FD-46BE-97FD-9938193CB7D1} = {344A1096-CC05-42D1-BDDB-E254C27508A9}
+ {1C86021F-E522-41A1-AC3F-01B0E474D75E} = {344A1096-CC05-42D1-BDDB-E254C27508A9}
+ {28132E4E-FFB9-45C4-99AD-1BCA04B3E597} = {344A1096-CC05-42D1-BDDB-E254C27508A9}
+ {4FA11334-D727-4C14-9D71-1DB46AC3BE9C} = {344A1096-CC05-42D1-BDDB-E254C27508A9}
+ {99679BF9-B888-4814-8E85-F751984CAFF8} = {344A1096-CC05-42D1-BDDB-E254C27508A9}
+ EndGlobalSection
+EndGlobal
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo.Abp.Studio.Analyzing.Abstractions.csproj b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo.Abp.Studio.Analyzing.Abstractions.csproj
new file mode 100644
index 0000000000..2b0847ae72
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo.Abp.Studio.Analyzing.Abstractions.csproj
@@ -0,0 +1,12 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/AbpStudioAnalyzingAbstractionsModule.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/AbpStudioAnalyzingAbstractionsModule.cs
new file mode 100644
index 0000000000..6935ae3755
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/AbpStudioAnalyzingAbstractionsModule.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.Modularity;
+
+namespace Volo.Abp.Studio.Analyzing
+{
+ public class AbpStudioAnalyzingAbstractionsModule : AbpModule
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Application/ApplicationServiceModel.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Application/ApplicationServiceModel.cs
new file mode 100644
index 0000000000..3618b58711
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Application/ApplicationServiceModel.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using JetBrains.Annotations;
+
+namespace Volo.Abp.Studio.Analyzing.Models.Application
+{
+ [PackageContentItemName(ContentTypeName)]
+ public class ApplicationServiceModel : PackageContentItemModel
+ {
+ public const string ContentTypeName = "applicationService";
+
+ public string Namespace { get; set; }
+
+ public string Summary { get; set; }
+
+ public List ImplementingInterfaces { get; set; }
+
+ public ApplicationServiceModel([NotNull] string name) : base(name)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Authorization/PermissionModel.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Authorization/PermissionModel.cs
new file mode 100644
index 0000000000..a386f5c4d5
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Authorization/PermissionModel.cs
@@ -0,0 +1,24 @@
+using JetBrains.Annotations;
+
+namespace Volo.Abp.Studio.Analyzing.Models.Authorization
+{
+ [PackageContentItemName(ContentTypeName)]
+ public class PermissionModel : PackageContentItemModel
+ {
+ public const string ContentTypeName = "permission";
+
+ public string DisplayName { get; }
+
+ public bool IsEnabled { get; }
+
+ public PermissionModel(
+ [NotNull] string name,
+ string displayName,
+ bool isEnabled)
+ : base(name)
+ {
+ DisplayName = displayName;
+ IsEnabled = isEnabled;
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Database/DatabaseCollectionModel.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Database/DatabaseCollectionModel.cs
new file mode 100644
index 0000000000..5e78f54f87
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Database/DatabaseCollectionModel.cs
@@ -0,0 +1,17 @@
+using JetBrains.Annotations;
+
+namespace Volo.Abp.Studio.Analyzing.Models.Database
+{
+ [PackageContentItemName(ContentTypeName)]
+ public class DatabaseCollectionModel : PackageContentItemModel
+ {
+ public const string ContentTypeName = "databaseCollection";
+
+ public string EntityFullName { get; private set; }
+
+ public DatabaseCollectionModel([NotNull] string name, string entityFullName) : base(name)
+ {
+ EntityFullName = Check.NotNullOrWhiteSpace(entityFullName, nameof(entityFullName));
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Database/DatabaseTableModel.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Database/DatabaseTableModel.cs
new file mode 100644
index 0000000000..a2af1acdbb
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Database/DatabaseTableModel.cs
@@ -0,0 +1,17 @@
+using JetBrains.Annotations;
+
+namespace Volo.Abp.Studio.Analyzing.Models.Database
+{
+ [PackageContentItemName(ContentTypeName)]
+ public class DatabaseTableModel : PackageContentItemModel
+ {
+ public const string ContentTypeName = "databaseTable";
+
+ public string EntityFullName { get; private set; }
+
+ public DatabaseTableModel([NotNull] string name, string entityFullName) : base(name)
+ {
+ EntityFullName = Check.NotNullOrWhiteSpace(entityFullName, nameof(entityFullName));
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Database/EfCoreDbContextModel.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Database/EfCoreDbContextModel.cs
new file mode 100644
index 0000000000..292b58e65f
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Database/EfCoreDbContextModel.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using JetBrains.Annotations;
+
+namespace Volo.Abp.Studio.Analyzing.Models.Database
+{
+ [PackageContentItemName(ContentTypeName)]
+ public class EfCoreDbContextModel : PackageContentItemModel, IDbContextModel
+ {
+ public const string ContentTypeName = "efCoreDbContext";
+
+ public string Namespace { get; private set; }
+
+ public string ConnectionStringName { get; set; }
+
+ public List DatabaseTables { get; set; }
+
+ public EfCoreDbContextModel(
+ [NotNull] string name,
+ [NotNull] string @namespace
+ ) : base(name)
+ {
+ Namespace = Check.NotNullOrWhiteSpace(@namespace, nameof(@namespace));
+ }
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Database/IDbContextModel.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Database/IDbContextModel.cs
new file mode 100644
index 0000000000..08c00162f5
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Database/IDbContextModel.cs
@@ -0,0 +1,7 @@
+namespace Volo.Abp.Studio.Analyzing.Models.Database
+{
+ public interface IDbContextModel
+ {
+ string ConnectionStringName { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Database/MongoDbContextModel.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Database/MongoDbContextModel.cs
new file mode 100644
index 0000000000..6fa6c80f79
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Database/MongoDbContextModel.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using JetBrains.Annotations;
+
+namespace Volo.Abp.Studio.Analyzing.Models.Database
+{
+ [PackageContentItemName(ContentTypeName)]
+ public class MongoDbContextModel : PackageContentItemModel, IDbContextModel
+ {
+ public const string ContentTypeName = "mongoDbContext";
+
+ public string Namespace { get; private set; }
+
+ public string ConnectionStringName { get; set; }
+
+ public List DatabaseCollections { get; set; }
+
+ public MongoDbContextModel(
+ [NotNull] string name,
+ [NotNull] string @namespace
+ ) : base(name)
+ {
+ Namespace = Check.NotNullOrWhiteSpace(@namespace, nameof(@namespace));
+ }
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Domain/AggregateRootModel.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Domain/AggregateRootModel.cs
new file mode 100644
index 0000000000..f31e07d8b1
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Domain/AggregateRootModel.cs
@@ -0,0 +1,12 @@
+namespace Volo.Abp.Studio.Analyzing.Models.Domain
+{
+ [PackageContentItemName(ContentTypeName)]
+ public class AggregateRootModel : EntityModel
+ {
+ public new const string ContentTypeName = "aggregateRoot";
+
+ public AggregateRootModel(string name) : base(name)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Domain/DomainServiceModel.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Domain/DomainServiceModel.cs
new file mode 100644
index 0000000000..6b4a648bcd
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Domain/DomainServiceModel.cs
@@ -0,0 +1,18 @@
+using JetBrains.Annotations;
+
+namespace Volo.Abp.Studio.Analyzing.Models.Domain
+{
+ [PackageContentItemName(ContentTypeName)]
+ public class DomainServiceModel : PackageContentItemModel
+ {
+ public const string ContentTypeName = "domainService";
+
+ public string Namespace { get; set; }
+
+ public string Summary { get; set; }
+
+ public DomainServiceModel([NotNull] string name) : base(name)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Domain/EntityModel.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Domain/EntityModel.cs
new file mode 100644
index 0000000000..0b3e5a5708
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Domain/EntityModel.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+
+namespace Volo.Abp.Studio.Analyzing.Models.Domain
+{
+ [PackageContentItemName(ContentTypeName)]
+ public class EntityModel : PackageContentItemModel
+ {
+ public const string ContentTypeName = "entity";
+
+ public string Namespace { get; set; }
+
+ public string PrimaryKeyType { get; set; }
+
+ public string Summary { get; set; }
+
+ public List CollectionProperties { get; set; }
+ public List NavigationProperties { get; set; }
+
+ public EntityModel(string name) : base(name)
+ {
+ CollectionProperties = new List();
+ NavigationProperties = new List();
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Domain/RepositoryInterfaceModel.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Domain/RepositoryInterfaceModel.cs
new file mode 100644
index 0000000000..a44613b335
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Domain/RepositoryInterfaceModel.cs
@@ -0,0 +1,20 @@
+using JetBrains.Annotations;
+
+namespace Volo.Abp.Studio.Analyzing.Models.Domain
+{
+ [PackageContentItemName(ContentTypeName)]
+ public class RepositoryInterfaceModel : PackageContentItemModel
+ {
+ public const string ContentTypeName = "repositoryInterface";
+
+ public string Namespace { get; set; }
+
+ public string Summary { get; set; }
+
+ public EntityModel EntityModel { get; set; }
+
+ public RepositoryInterfaceModel([NotNull] string name) : base(name)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Feature/FeatureModel.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Feature/FeatureModel.cs
new file mode 100644
index 0000000000..ee2b7ef48b
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Feature/FeatureModel.cs
@@ -0,0 +1,40 @@
+using JetBrains.Annotations;
+
+namespace Volo.Abp.Studio.Analyzing.Models.Feature
+{
+ [PackageContentItemName(ContentTypeName)]
+ public class FeatureModel : PackageContentItemModel
+ {
+ public const string ContentTypeName = "feature";
+
+ public string ValueType { get; }
+
+ public string DefaultValue { get; }
+
+ public string DisplayName { get; }
+
+ public string Description { get; }
+
+ public bool IsAvailableToHost { get; }
+
+ public bool IsVisibleToClients { get; }
+
+ public FeatureModel(
+ [NotNull] string name,
+ [NotNull] string valueType,
+ string defaultValue,
+ string displayName,
+ string description,
+ bool isAvailableToHost,
+ bool isVisibleToClients
+ ) : base(name)
+ {
+ ValueType = valueType;
+ DefaultValue = defaultValue;
+ DisplayName = displayName;
+ Description = description;
+ IsAvailableToHost = isAvailableToHost;
+ IsVisibleToClients = isVisibleToClients;
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Module/AbpModuleModel.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Module/AbpModuleModel.cs
new file mode 100644
index 0000000000..9094d7d0b8
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Module/AbpModuleModel.cs
@@ -0,0 +1,16 @@
+using JetBrains.Annotations;
+
+namespace Volo.Abp.Studio.Analyzing.Models.Module
+{
+ [PackageContentItemName(ContentTypeName)]
+ public class AbpModuleModel : PackageContentItemModel
+ {
+ public const string ContentTypeName = "abpModule";
+
+ public string Namespace { get; set; }
+
+ public AbpModuleModel([NotNull] string name) : base(name)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/PackageContentItemModel.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/PackageContentItemModel.cs
new file mode 100644
index 0000000000..749ecd8fa9
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/PackageContentItemModel.cs
@@ -0,0 +1,16 @@
+using JetBrains.Annotations;
+
+namespace Volo.Abp.Studio.Analyzing.Models
+{
+ public abstract class PackageContentItemModel
+ {
+ public string ContentType { get; }
+ public string Name { get; }
+
+ public PackageContentItemModel([NotNull] string name)
+ {
+ Name = Check.NotNullOrWhiteSpace(name, nameof(name));
+ ContentType = PackageContentItemNameAttribute.GetName(GetType());
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/PackageContentItemNameAttribute.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/PackageContentItemNameAttribute.cs
new file mode 100644
index 0000000000..9a04fa274b
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/PackageContentItemNameAttribute.cs
@@ -0,0 +1,26 @@
+using System;
+using Volo.Abp.Reflection;
+
+namespace Volo.Abp.Studio.Analyzing.Models
+{
+ public class PackageContentItemNameAttribute : Attribute
+ {
+ public string Name { get; }
+
+ public PackageContentItemNameAttribute(string name)
+ {
+ Name = name;
+ }
+
+ public static string GetName(Type type)
+ {
+ var attribute = ReflectionHelper.GetSingleAttributeOrDefault(type);
+ if (attribute == null)
+ {
+ throw new ApplicationException($"Given type {type.FullName} must have an {nameof(PackageContentItemNameAttribute)}, but not defined!");
+ }
+
+ return attribute.Name;
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/PackageContentList.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/PackageContentList.cs
new file mode 100644
index 0000000000..ab519cb264
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/PackageContentList.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+
+namespace Volo.Abp.Studio.Analyzing.Models
+{
+ public class PackageContentList : List
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/PackageModel.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/PackageModel.cs
new file mode 100644
index 0000000000..74bcaac9cf
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/PackageModel.cs
@@ -0,0 +1,20 @@
+using JetBrains.Annotations;
+
+namespace Volo.Abp.Studio.Analyzing.Models
+{
+ public class PackageModel
+ {
+ public string Name { get; }
+
+ public string Hash { get; }
+
+ public PackageContentList Contents { get; }
+
+ public PackageModel([NotNull] string name, [NotNull] string hash)
+ {
+ Name = Check.NotNullOrWhiteSpace(name, nameof(name));
+ Contents = new PackageContentList();
+ Hash = hash;
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Setting/SettingModel.cs b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Setting/SettingModel.cs
new file mode 100644
index 0000000000..910f81099f
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Analyzing.Abstractions/Volo/Abp/Studio/Analyzing/Models/Setting/SettingModel.cs
@@ -0,0 +1,26 @@
+using JetBrains.Annotations;
+
+namespace Volo.Abp.Studio.Analyzing.Models.Setting
+{
+ [PackageContentItemName(ContentTypeName)]
+ public class SettingModel : PackageContentItemModel
+ {
+ public const string ContentTypeName = "setting";
+
+ public string DefaultValue { get; set; }
+
+ public string DisplayName { get; set; }
+
+ public string Description { get; set; }
+
+ public bool IsVisibleToClient { get; set; }
+
+ public bool IsInherited { get; set; }
+
+ public bool IsEncrypted { get; set; }
+
+ public SettingModel([NotNull] string name) : base(name)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo.Abp.Studio.Domain.CommonServices.csproj b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo.Abp.Studio.Domain.CommonServices.csproj
new file mode 100644
index 0000000000..b63a2e30be
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo.Abp.Studio.Domain.CommonServices.csproj
@@ -0,0 +1,21 @@
+
+
+
+
+
+ net5.0
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/AbpStudioDomainCommonServicesModule.cs b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/AbpStudioDomainCommonServicesModule.cs
new file mode 100644
index 0000000000..84b2dba930
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/AbpStudioDomainCommonServicesModule.cs
@@ -0,0 +1,17 @@
+using System.IO.Abstractions;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Modularity;
+
+namespace Volo.Abp.Studio
+{
+ [DependsOn(
+ typeof(AbpStudioDomainSharedModule)
+ )]
+ public class AbpStudioDomainCommonServicesModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddSingleton(new FileSystem());
+ }
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/AbpStudioException.cs b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/AbpStudioException.cs
new file mode 100644
index 0000000000..418e6067b9
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/AbpStudioException.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Runtime.Serialization;
+using Microsoft.Extensions.Logging;
+
+namespace Volo.Abp.Studio
+{
+ public class AbpStudioException : BusinessException
+ {
+ public AbpStudioException(
+ string code = null,
+ string message = null,
+ string details = null,
+ Exception innerException = null,
+ LogLevel logLevel = LogLevel.Warning)
+ : base(code, message, details, innerException, logLevel)
+ {
+ Code = code;
+ Details = details;
+ LogLevel = logLevel;
+ }
+
+ public AbpStudioException(SerializationInfo serializationInfo, StreamingContext context)
+ : base(serializationInfo, context)
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Helpers/CmdHelper.cs b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Helpers/CmdHelper.cs
new file mode 100644
index 0000000000..3b8b8f0775
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Helpers/CmdHelper.cs
@@ -0,0 +1,103 @@
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.Studio.Helpers
+{
+ //todo: move this to volo.abp.cli.core!
+ public class CmdHelper : ICmdHelper, ITransientDependency
+ {
+ public void RunCmd(string command, string workingDirectory = null)
+ {
+ var procStartInfo = new ProcessStartInfo(
+ GetFileName(),
+ GetArguments(command)
+ );
+
+ if (!string.IsNullOrEmpty(workingDirectory))
+ {
+ procStartInfo.WorkingDirectory = workingDirectory;
+ }
+
+ using (var process = Process.Start(procStartInfo))
+ {
+ process?.WaitForExit();
+ }
+ }
+
+ public string RunCmdAndGetOutput(string command, string workingDirectory = null)
+ {
+ string output;
+
+ using (var process = new Process())
+ {
+ process.StartInfo = new ProcessStartInfo(GetFileName())
+ {
+ Arguments = GetArguments(command),
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true
+ };
+
+ if (!string.IsNullOrEmpty(workingDirectory))
+ {
+ process.StartInfo.WorkingDirectory = workingDirectory;
+ }
+
+ process.Start();
+
+ using (var standardOutput = process.StandardOutput)
+ {
+ using (var standardError = process.StandardError)
+ {
+ output = standardOutput.ReadToEnd();
+ output += standardError.ReadToEnd();
+ }
+ }
+
+ process.WaitForExit();
+ }
+
+ return output.Trim();
+ }
+
+ private string GetArguments(string command)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ return "-c \"" + command + "\"";
+ }
+
+ //Windows default.
+ return "/C \"" + command + "\"";
+ }
+
+ private string GetFileName()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ //Windows
+ return "cmd.exe";
+ }
+
+ //Linux or OSX
+ if (File.Exists("/bin/bash"))
+ {
+ return "/bin/bash";
+ }
+
+ if (File.Exists("/bin/sh"))
+ {
+ return "/bin/sh"; //some Linux distributions like Alpine doesn't have bash
+ }
+
+ throw new AbpException($"Cannot determine shell command for this OS! " +
+ $"Running on OS: {System.Runtime.InteropServices.RuntimeInformation.OSDescription} | " +
+ $"OS Architecture: {System.Runtime.InteropServices.RuntimeInformation.OSArchitecture} | " +
+ $"Framework: {System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription} | " +
+ $"Process Architecture{System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture}");
+ }
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Helpers/ICmdHelper.cs b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Helpers/ICmdHelper.cs
new file mode 100644
index 0000000000..f91bc7e69a
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Helpers/ICmdHelper.cs
@@ -0,0 +1,9 @@
+namespace Volo.Abp.Studio.Helpers
+{
+ public interface ICmdHelper
+ {
+ void RunCmd(string command, string workingDirectory = null);
+
+ string RunCmdAndGetOutput(string command, string workingDirectory = null);
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Helpers/PathHelper.cs b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Helpers/PathHelper.cs
new file mode 100644
index 0000000000..b741e37cf8
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Helpers/PathHelper.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Runtime.InteropServices;
+using JetBrains.Annotations;
+
+namespace Volo.Abp.Studio.Helpers
+{
+ public static class PathHelper
+ {
+ public static string GetRelativePath([NotNull] string basePath, [NotNull] string targetPath)
+ {
+ Check.NotNull(basePath, nameof(basePath));
+ Check.NotNull(targetPath, nameof(targetPath));
+
+ return new Uri(basePath).MakeRelativeUri(new Uri(targetPath)).ToString();
+ }
+
+ public static string EnsureForwardSlash(string path)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return path.Replace("\\", "/");
+ }
+
+ return path;
+ }
+
+ public static string Normalize(string path)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return path.Replace("/", "\\");
+ }
+
+ return path;
+ }
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Modifying/AbpModuleFileManager.cs b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Modifying/AbpModuleFileManager.cs
new file mode 100644
index 0000000000..ccb90ad2e4
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Modifying/AbpModuleFileManager.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.IO.Abstractions;
+using System.Linq;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.Studio.Packages.Modifying
+{
+ public class AbpModuleFileManager : IAbpModuleFileManager, ITransientDependency
+ {
+ public IFileSystem FileSystem { get; }
+
+ public AbpModuleFileManager(IFileSystem fileSystem)
+ {
+ FileSystem = fileSystem;
+ }
+
+ public async Task AddDependency(string filePath, string moduleToAdd)
+ {
+ var fileContent = await FileSystem.File.ReadAllTextAsync(filePath);
+ var moduleName = moduleToAdd.Split(".").Last();
+ var moduleNamespace = moduleToAdd.RemovePostFix("." + moduleName);
+ var usingStatement = $"using {moduleNamespace};";
+ var dependsOnStart = $"DependsOn(";
+
+ if (fileContent.Contains($"typeof({moduleName})"))
+ {
+ // module already added
+ return;
+ }
+
+ if (!fileContent.NormalizeLineEndings().SplitToLines().Any(l=> l.Trim().StartsWith("namespace ") && l.Contains(moduleNamespace)) &&
+ !fileContent.Contains(usingStatement)
+ )
+ {
+ fileContent = usingStatement + Environment.NewLine + fileContent;
+ }
+
+ if (!fileContent.Contains(dependsOnStart))
+ {
+ var fileLines = fileContent.NormalizeLineEndings().SplitToLines().ToList();
+ fileLines.InsertBefore(l=> l.Contains("public") && l.Contains("class"), $" [DependsOn(typeof({moduleName}))]");
+ fileContent = fileLines.JoinAsString(Environment.NewLine);
+ }
+ else
+ {
+ var fileLines = fileContent.NormalizeLineEndings().SplitToLines().ToList();
+ var dependsOnStartLine = fileLines.First(l=> l.Contains(dependsOnStart));
+ var dependsOnStartLineModified = dependsOnStartLine.Replace(dependsOnStart,
+ dependsOnStart + Environment.NewLine +
+ $" typeof({moduleName}),"
+ );
+ fileContent = fileContent.Replace(dependsOnStartLine, dependsOnStartLineModified);
+ }
+
+ FileSystem.File.WriteAllTextAsync(filePath, fileContent);
+ }
+
+ public async Task ExtractModuleNameWithNamespace(string filePath)
+ {
+ var fileContent = await FileSystem.File.ReadAllTextAsync(filePath);
+
+ var fileLines = fileContent.NormalizeLineEndings().SplitToLines();
+
+ var lineOfClassName = fileLines.First(l=> l.Contains("public") && l.Contains("class"));
+
+ var moduleName = lineOfClassName.Split(":")[0].Trim().Split(" ").Last();
+
+ var lineOfNamespace = fileLines.First(l=> l.Trim().StartsWith("namespace"));
+
+ var moduleNamespace = lineOfNamespace.Split(" ").Skip(1).First();
+
+ return moduleNamespace + "." + moduleName;
+ }
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Modifying/CsprojFileManager.cs b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Modifying/CsprojFileManager.cs
new file mode 100644
index 0000000000..4087f5d6ae
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Modifying/CsprojFileManager.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Threading.Tasks;
+using System.Xml;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Studio.Helpers;
+using Volo.Abp.Studio.Xml;
+
+namespace Volo.Abp.Studio.Packages.Modifying
+{
+ public class CsprojFileManager: XmlFileManagerBase, ICsprojFileManager, ITransientDependency
+ {
+ public async Task AddProjectReferenceAsync(string filePath, string projectToReference)
+ {
+ var document = await GetXmlDocumentAsync(filePath);
+
+ if (document.SelectNodes($"/Project/ItemGroup/ProjectReference[starts-with(@Include, '{projectToReference}')]").Count > 0)
+ {
+ // Project reference is already added.
+ return;
+ }
+
+ var relativePath = PathHelper.GetRelativePath(filePath, projectToReference);
+
+ var itemGroupNode = GetOrCreateItemGroupNode(document);
+
+ var newNode = document.CreateElement("ProjectReference");
+
+ var includeAttr = document.CreateAttribute("Include");
+ includeAttr.Value = relativePath;
+ newNode.Attributes.Append(includeAttr);
+
+ itemGroupNode.AppendChild(newNode);
+
+ await SaveXmlDocumentAsync(filePath, document);
+ }
+
+ public async Task AddPackageReferenceAsync(string filePath, string packageName, string version)
+ {
+ var document = await GetXmlDocumentAsync(filePath);
+
+ if (document.SelectNodes($"/Project/ItemGroup/PackageReference[starts-with(@Include, '{packageName}')]").Count > 0)
+ {
+ // Package reference is already added.
+ return;
+ }
+
+ var itemGroupNode = GetOrCreateItemGroupNode(document);
+
+ var newNode = document.CreateElement("PackageReference");
+
+ var includeAttr = document.CreateAttribute("Include");
+ includeAttr.Value = packageName;
+ newNode.Attributes.Append(includeAttr);
+
+ var versionAttr = document.CreateAttribute("Version");
+ versionAttr.Value = version;
+ newNode.Attributes.Append(versionAttr);
+
+ itemGroupNode.AppendChild(newNode);
+ itemGroupNode.AppendChild(document.CreateWhitespace(Environment.NewLine + " "));
+
+ await SaveXmlDocumentAsync(filePath, document);
+ }
+
+ private XmlNode GetOrCreateItemGroupNode(XmlDocument document)
+ {
+ var nodes = document["Project"].SelectNodes("ItemGroup");
+
+ if (nodes == null || nodes.Count < 1)
+ {
+ var newNode = document.CreateElement("ItemGroup");
+ document["Project"].AppendChild(newNode);
+ return newNode;
+ }
+
+ foreach (XmlNode node in nodes)
+ {
+ if (node.SelectNodes("ProjectReference").Count > 0)
+ {
+ return node;
+ }
+ }
+
+ foreach (XmlNode node in nodes)
+ {
+ if (node.SelectNodes("PackageReference").Count > 0)
+ {
+ return node;
+ }
+ }
+
+ return nodes[0];
+ }
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Modifying/IAbpModuleFileManager.cs b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Modifying/IAbpModuleFileManager.cs
new file mode 100644
index 0000000000..7eb2060e48
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Modifying/IAbpModuleFileManager.cs
@@ -0,0 +1,11 @@
+using System.Threading.Tasks;
+
+namespace Volo.Abp.Studio.Packages.Modifying
+{
+ public interface IAbpModuleFileManager
+ {
+ Task AddDependency(string filePath, string moduleToAdd);
+
+ Task ExtractModuleNameWithNamespace(string filePath);
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Modifying/ICsprojFileManager.cs b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Modifying/ICsprojFileManager.cs
new file mode 100644
index 0000000000..29aa3f34b3
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Modifying/ICsprojFileManager.cs
@@ -0,0 +1,11 @@
+using System.Threading.Tasks;
+
+namespace Volo.Abp.Studio.Packages.Modifying
+{
+ public interface ICsprojFileManager
+ {
+ Task AddProjectReferenceAsync(string filePath, string projectToReference);
+
+ Task AddPackageReferenceAsync(string filePath, string packageName, string version);
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Nuget/INugetSourceCodeStore.cs b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Nuget/INugetSourceCodeStore.cs
new file mode 100644
index 0000000000..6fce22dbe0
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Nuget/INugetSourceCodeStore.cs
@@ -0,0 +1,18 @@
+using System.Threading.Tasks;
+
+namespace Volo.Abp.Studio.Nuget
+{
+ public interface INugetSourceCodeStore
+ {
+ Task GetCachedSourceCodeFilePathAsync(string name,
+ string type,
+ string version = null,
+ bool includePreReleases = false);
+
+ Task GetCachedDllFilePathAsync(string name,
+ string type,
+ string version = null,
+ bool includePreReleases = false,
+ bool includeDependencies = false);
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Nuget/NugetPackageCacheManager.cs b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Nuget/NugetPackageCacheManager.cs
new file mode 100644
index 0000000000..935ed85573
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Nuget/NugetPackageCacheManager.cs
@@ -0,0 +1,35 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Studio.Helpers;
+
+namespace Volo.Abp.Studio.Nuget
+{
+ public class NugetPackageCacheManager : ITransientDependency
+ {
+ private readonly ICmdHelper _cmdHelper;
+
+ public NugetPackageCacheManager(ICmdHelper cmdHelper)
+ {
+ _cmdHelper = cmdHelper;
+ }
+
+ public async Task CachePackageAsync(string packageName, string version, bool deleteAfter = true)
+ {
+ var temporaryFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
+
+ Directory.CreateDirectory(temporaryFolder);
+
+ _cmdHelper.RunCmdAndGetOutput("dotnet new console -lang c#", temporaryFolder);
+ _cmdHelper.RunCmdAndGetOutput($"dotnet add package {packageName} --version {version}", temporaryFolder);
+
+ if (deleteAfter)
+ {
+ Directory.Delete(temporaryFolder, true);
+ }
+
+ return temporaryFolder;
+ }
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Nuget/NugetSourceCodeStore.cs b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Nuget/NugetSourceCodeStore.cs
new file mode 100644
index 0000000000..52613f2f49
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Nuget/NugetSourceCodeStore.cs
@@ -0,0 +1,151 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Volo.Abp.Cli.NuGet;
+using Volo.Abp.Cli.ProjectBuilding;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Studio.Helpers;
+
+namespace Volo.Abp.Studio.Nuget
+{
+ [Dependency(ReplaceServices = true)]
+ public class NugetSourceCodeStore : ISourceCodeStore, INugetSourceCodeStore, ITransientDependency
+ {
+ private readonly NuGetService _nuGetService;
+ private readonly NugetPackageCacheManager _nugetPackageCacheManager;
+ private readonly ICmdHelper _cmdHelper;
+
+ public NugetSourceCodeStore(
+ NuGetService nuGetService,
+ NugetPackageCacheManager nugetPackageCacheManager,
+ ICmdHelper cmdHelper)
+ {
+ _nuGetService = nuGetService;
+ _nugetPackageCacheManager = nugetPackageCacheManager;
+ _cmdHelper = cmdHelper;
+ }
+
+ public async Task GetAsync(
+ string name,
+ string type,
+ string version = null,
+ string templateSource = null,
+ bool includePreReleases = false)
+ {
+ if (type == SourceCodeTypes.Template)
+ {
+ name = TemplateNugetPackageInfoProvider.GetNugetPackageName(name);
+ }
+
+ var latestVersion = await GetLatestVersionAsync(name, includePreReleases);
+
+ if (version == null)
+ {
+ version = latestVersion;
+ }
+
+ var localCachedFilePath = await GetLocalCacheSourceCodeFilePathInternal(name, version);
+
+ return new TemplateFile(await File.ReadAllBytesAsync(localCachedFilePath), version, latestVersion, latestVersion);
+ }
+
+ public async Task GetCachedSourceCodeFilePathAsync(string name, string type, string version = null,
+ bool includePreReleases = false)
+ {
+ if (type == SourceCodeTypes.Template)
+ {
+ name = TemplateNugetPackageInfoProvider.GetNugetPackageName(name);
+ }
+
+ if (version == null)
+ {
+ version = await GetLatestVersionAsync(name, includePreReleases);
+ }
+
+ return await GetLocalCacheSourceCodeFilePathInternal(name, version);
+ }
+
+ public async Task GetCachedDllFilePathAsync(string name, string type, string version = null, bool includePreReleases = false, bool includeDependencies = false)
+ {
+ if (type == SourceCodeTypes.Template)
+ {
+ name = TemplateNugetPackageInfoProvider.GetNugetPackageName(name);
+ }
+
+ if (version == null)
+ {
+ version = await GetLatestVersionAsync(name, includePreReleases);
+ }
+
+ var localDllFolder = Path.Combine(
+ GetLocalNugetCachePath(),
+ name,
+ version,
+ "lib");
+
+ if (!Directory.Exists(localDllFolder) ||
+ (includeDependencies && !Directory.GetFiles(localDllFolder, $"*Volo.Abp.Studio.ModuleInstaller.dll", SearchOption.AllDirectories).Any()))
+ {
+ if (includeDependencies)
+ {
+ var temporaryFolder = await _nugetPackageCacheManager.CachePackageAsync(name, version, false);
+
+ var outputFolder = Path.GetDirectoryName(
+ Directory
+ .GetFiles(localDllFolder, $"*{name}.dll", SearchOption.AllDirectories)
+ .First()
+ );
+
+ _cmdHelper.RunCmdAndGetOutput($"dotnet build -o {outputFolder}", temporaryFolder);
+
+ Directory.Delete(temporaryFolder, true);
+ }
+ else
+ {
+ await _nugetPackageCacheManager.CachePackageAsync(name, version);
+ }
+ }
+
+ if (!Directory.Exists(localDllFolder))
+ {
+ return null;
+ }
+
+ return Directory.GetFiles(localDllFolder, $"{name}.dll", SearchOption.AllDirectories).First();
+ }
+
+ private async Task GetLatestVersionAsync(string nugetPackage, bool includePreReleases)
+ {
+ var v = await _nuGetService.GetLatestVersionOrNullAsync(nugetPackage, includePreReleases);
+
+ return v.ToString();
+ }
+
+ private async Task GetLocalCacheSourceCodeFilePathInternal(string name, string version)
+ {
+ var localCacheFile = Path.Combine(
+ GetLocalNugetCachePath(),
+ name,
+ version,
+ "content",
+ $"{name}.zip");
+
+
+ if (!File.Exists(localCacheFile))
+ {
+ await _nugetPackageCacheManager.CachePackageAsync(name, version);
+ }
+
+ return localCacheFile;
+ }
+
+ private string GetLocalNugetCachePath()
+ {
+ return Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
+ ".nuget",
+ "packages");
+ }
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Nuget/TemplateNugetPackageInfoProvider.cs b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Nuget/TemplateNugetPackageInfoProvider.cs
new file mode 100644
index 0000000000..7654279fec
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Nuget/TemplateNugetPackageInfoProvider.cs
@@ -0,0 +1,18 @@
+using System.Threading.Tasks;
+
+namespace Volo.Abp.Studio.Nuget
+{
+ public static class TemplateNugetPackageInfoProvider
+ {
+ public static string GetNugetPackageName(string template)
+ {
+ switch (template)
+ {
+ case "app":
+ return "Cotur.Basic.Template"; // todo: replace with real template!
+ default:
+ return null;
+ }
+ }
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Xml/XmlFileManagerBase.cs b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Xml/XmlFileManagerBase.cs
new file mode 100644
index 0000000000..74eb5379c2
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.CommonServices/Volo/Abp/Studio/Xml/XmlFileManagerBase.cs
@@ -0,0 +1,51 @@
+using System;
+using System.IO;
+using System.IO.Abstractions;
+using System.Threading.Tasks;
+using System.Xml;
+using System.Xml.Linq;
+using Volo.Abp.Threading;
+
+namespace Volo.Abp.Studio.Xml
+{
+ public abstract class XmlFileManagerBase
+ {
+ public IFileSystem FileSystem { get; set; }
+
+ public ICancellationTokenProvider CancellationTokenProvider { get; set; }
+
+ protected async Task GetXmlDocumentAsync(string filePath)
+ {
+ try
+ {
+ var doc = new XmlDocument() { PreserveWhitespace = true };
+ doc.Load(GenerateStreamFromString(await FileSystem.File.ReadAllTextAsync(filePath)));
+ return doc;
+ }
+ catch (Exception ex)
+ {
+ throw new AbpException($"Error while reading {filePath} as XML document.", innerException: ex);
+ }
+ }
+
+ protected async Task SaveXmlDocumentAsync(string filePath, XmlDocument rootNode)
+ {
+ await SaveFileContentAsync(filePath, XDocument.Parse(rootNode.OuterXml).ToString());
+ }
+
+ protected async Task SaveFileContentAsync(string filePath, string content)
+ {
+ await FileSystem.File.WriteAllTextAsync(filePath, content, CancellationTokenProvider.Token);
+ }
+
+ private MemoryStream GenerateStreamFromString(string s)
+ {
+ var stream = new MemoryStream();
+ var writer = new StreamWriter(stream);
+ writer.Write(s);
+ writer.Flush();
+ stream.Position = 0;
+ return stream;
+ }
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.Domain.Shared/Volo.Abp.Studio.Domain.Shared.csproj b/studio/src/Volo.Abp.Studio.Domain.Shared/Volo.Abp.Studio.Domain.Shared.csproj
new file mode 100644
index 0000000000..23144285d3
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.Shared/Volo.Abp.Studio.Domain.Shared.csproj
@@ -0,0 +1,15 @@
+
+
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
diff --git a/studio/src/Volo.Abp.Studio.Domain.Shared/Volo/Abp/Studio/AbpStudioDomainSharedModule.cs b/studio/src/Volo.Abp.Studio.Domain.Shared/Volo/Abp/Studio/AbpStudioDomainSharedModule.cs
new file mode 100644
index 0000000000..9693bc2577
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.Shared/Volo/Abp/Studio/AbpStudioDomainSharedModule.cs
@@ -0,0 +1,13 @@
+using Volo.Abp.Cli;
+using Volo.Abp.Modularity;
+
+namespace Volo.Abp.Studio
+{
+ [DependsOn(
+ typeof(AbpCliCoreModule)
+ )]
+ public class AbpStudioDomainSharedModule : AbpModule
+ {
+
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.Domain.Shared/Volo/Abp/Studio/AbpStudioErrorCodes.cs b/studio/src/Volo.Abp.Studio.Domain.Shared/Volo/Abp/Studio/AbpStudioErrorCodes.cs
new file mode 100644
index 0000000000..e687f840d7
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.Domain.Shared/Volo/Abp/Studio/AbpStudioErrorCodes.cs
@@ -0,0 +1,30 @@
+namespace Volo.Abp.Studio
+{
+ public static class AbpStudioErrorCodes
+ {
+ public const string SolutionAlreadyExists = "AbpStudio:SolutionAlreadyExists";
+ public const string ModuleAlreadyExistsInTheSolution = "AbpStudio:ModuleAlreadyExistsInTheSolution";
+ public const string ModuleFileAlreadyExists = "AbpStudio:ModuleFileAlreadyExists";
+ public const string IncorrectFileFormat = "AbpStudio:IncorrectFileFormat";
+ public const string ModuleNotFound = "AbpStudio:ModuleNotFound";
+ public const string ModuleNotSpecified = "AbpStudio:ModuleNotSpecified";
+ public const string SolutionNotSpecified = "AbpStudio:SolutionNotSpecified";
+ public const string ProjectAlreadyExistInTheModule = "AbpStudio:ProjectAlreadyExistInTheModule";
+ public const string IncorrectSolutionFileFormat = "AbpStudio:IncorrectSolutionFileFormat";
+ public const string FolderNotFound = "AbpStudio:FolderNotFound";
+ public const string ProjectWithSameNameAlreadyExistInTheSolutionFile = "AbpStudio:ProjectWithSameNameAlreadyExistInTheSolutionFile";
+ public const string UndefinedPackageTemplate = "AbpStudio:UndefinedPackageTemplate";
+ public const string PackageTemplateNotSpecified = "AbpStudio:PackageTemplateNotSpecified";
+ public const string PackageNameMustBeSpecified = "AbpStudio:PackageNameMustBeSpecified";
+ public const string FileAlreadyExists = "AbpStudio:FileAlreadyExists";
+ public const string PackageNotSpecified = "AbpStudio:PackageNotSpecified";
+ public const string DbmsMustBeSpecified = "AbpStudio:DbmsMustBeSpecified";
+ public const string UserNotLoggedIn = "AbpStudio:UserNotLoggedIn";
+ public const string PackageAlreadyExist = "AbpStudio:PackageAlreadyExist";
+ public const string AbpModuleFileNotFound = "AbpStudio:AbpModuleFileNotFound";
+ public const string DllNotFound = "AbpStudio:DllNotFound";
+ public const string PackageNotFound = "AbpStudio:PackageNotFound";
+ public const string FileNotFound = "AbpStudio:FileNotFound";
+ public const string IncorrectFolderName = "AbpStudio:IncorrectFolderName ";
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo.Abp.Studio.ModuleInstaller.Abstractions.csproj b/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo.Abp.Studio.ModuleInstaller.Abstractions.csproj
new file mode 100644
index 0000000000..143944d975
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo.Abp.Studio.ModuleInstaller.Abstractions.csproj
@@ -0,0 +1,13 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
diff --git a/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/AbpStudioModuleInstallerAbstractionsModule.cs b/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/AbpStudioModuleInstallerAbstractionsModule.cs
new file mode 100644
index 0000000000..c9e68ec940
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/AbpStudioModuleInstallerAbstractionsModule.cs
@@ -0,0 +1,13 @@
+using Volo.Abp.Modularity;
+using Volo.Abp.Studio.Analyzing;
+
+namespace Volo.Abp.Studio
+{
+ [DependsOn(
+ typeof(AbpStudioAnalyzingAbstractionsModule)
+ )]
+ public class AbpStudioModuleInstallerAbstractionsModule : AbpModule
+ {
+
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/Analyzing/AnalyzingOptions.cs b/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/Analyzing/AnalyzingOptions.cs
new file mode 100644
index 0000000000..7296dfce74
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/Analyzing/AnalyzingOptions.cs
@@ -0,0 +1,45 @@
+using JetBrains.Annotations;
+
+namespace Volo.Abp.Studio.Analyzing
+{
+ public class AnalyzingOptions
+ {
+ public bool Force { get; set; } = false;
+
+ [CanBeNull]
+ public string AnalyzeConfigurationFile { get; set; }
+
+ [CanBeNull]
+ public string SettingNamePrefix { get; set; }
+
+ [CanBeNull]
+ public string FeatureNamePrefix { get; set; }
+
+ // Combines two options
+ // The second option has more priority
+ public static AnalyzingOptions Combine([CanBeNull] AnalyzingOptions first, [CanBeNull] AnalyzingOptions second)
+ {
+ if (second == null && first == null)
+ {
+ return new AnalyzingOptions();
+ }
+
+ if (second == null)
+ {
+ return first;
+ }
+
+ if (first == null)
+ {
+ return second;
+ }
+
+ return new AnalyzingOptions
+ {
+ AnalyzeConfigurationFile = second.AnalyzeConfigurationFile ?? first.AnalyzeConfigurationFile,
+ SettingNamePrefix = second.SettingNamePrefix ?? first.SettingNamePrefix,
+ FeatureNamePrefix = second.FeatureNamePrefix ?? first.FeatureNamePrefix
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/ModuleInstalling/IModuleInstallingPipelineBuilder.cs b/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/ModuleInstalling/IModuleInstallingPipelineBuilder.cs
new file mode 100644
index 0000000000..1112328675
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/ModuleInstalling/IModuleInstallingPipelineBuilder.cs
@@ -0,0 +1,9 @@
+using System.Threading.Tasks;
+
+namespace Volo.Abp.Studio.ModuleInstalling
+{
+ public interface IModuleInstallingPipelineBuilder
+ {
+ Task BuildAsync(ModuleInstallingContext context);
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/ModuleInstalling/ModuleInstallingContext.cs b/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/ModuleInstalling/ModuleInstallingContext.cs
new file mode 100644
index 0000000000..06393b210e
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/ModuleInstalling/ModuleInstallingContext.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using JetBrains.Annotations;
+using Volo.Abp.Studio.Packages;
+
+namespace Volo.Abp.Studio.ModuleInstalling
+{
+ public class ModuleInstallingContext
+ {
+ public bool WithSourceCode { get; set; }
+
+ public bool AddToSolutionFile { get; set; }
+
+ public string Version { get; set; }
+
+ public List TargetModulePackages { get; protected set; }
+
+ public List ReferenceModulePackages { get; protected set; }
+
+ public Dictionary Options { get; }
+
+ public IServiceProvider ServiceProvider { get; }
+
+ public ModuleInstallingContext(
+ bool withSourceCode,
+ bool addToSolutionFile,
+ string version,
+ Dictionary options,
+ IServiceProvider serviceProvider)
+ {
+ WithSourceCode = withSourceCode;
+ AddToSolutionFile = addToSolutionFile;
+ Version = version;
+ Options = options;
+
+ TargetModulePackages = new List();
+ ReferenceModulePackages = new List();
+
+ ServiceProvider = Check.NotNull(serviceProvider, nameof(serviceProvider));
+ }
+
+ public void SetReferenceModulePackages([NotNull] List referenceModulePackages)
+ {
+ Check.NotNull(referenceModulePackages, nameof(referenceModulePackages));
+
+ ReferenceModulePackages = referenceModulePackages;
+ }
+
+ public void SetTargetModulePackages([NotNull] List targetModulePackages)
+ {
+ Check.NotNull(targetModulePackages, nameof(targetModulePackages));
+
+ TargetModulePackages = targetModulePackages;
+ }
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/ModuleInstalling/ModuleInstallingPipeline.cs b/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/ModuleInstalling/ModuleInstallingPipeline.cs
new file mode 100644
index 0000000000..82d77780f7
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/ModuleInstalling/ModuleInstallingPipeline.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace Volo.Abp.Studio.ModuleInstalling
+{
+ public class ModuleInstallingPipeline
+ {
+ public ModuleInstallingContext Context { get; }
+
+ public List Steps { get; }
+
+ public ModuleInstallingPipeline(ModuleInstallingContext context)
+ {
+ Context = context;
+ Steps = new List();
+ }
+
+ public async Task ExecuteAsync()
+ {
+ foreach (var step in Steps)
+ {
+ await step.ExecuteAsync(Context);
+ }
+ }
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/ModuleInstalling/ModuleInstallingPipelineStep.cs b/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/ModuleInstalling/ModuleInstallingPipelineStep.cs
new file mode 100644
index 0000000000..b98d32b32a
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.ModuleInstaller.Abstractions/Volo/Abp/Studio/ModuleInstalling/ModuleInstallingPipelineStep.cs
@@ -0,0 +1,9 @@
+using System.Threading.Tasks;
+
+namespace Volo.Abp.Studio.ModuleInstalling
+{
+ public abstract class ModuleInstallingPipelineStep
+ {
+ public abstract Task ExecuteAsync(ModuleInstallingContext context);
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo.Abp.Studio.ModuleInstaller.csproj b/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo.Abp.Studio.ModuleInstaller.csproj
new file mode 100644
index 0000000000..25d0e8cbeb
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo.Abp.Studio.ModuleInstaller.csproj
@@ -0,0 +1,16 @@
+
+
+
+
+
+ net5.0
+ true
+
+
+
+
+
+
+
+
+
diff --git a/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/AbpStudioModuleInstallerModule.cs b/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/AbpStudioModuleInstallerModule.cs
new file mode 100644
index 0000000000..3450ab8a5c
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/AbpStudioModuleInstallerModule.cs
@@ -0,0 +1,16 @@
+using Volo.Abp.Modularity;
+
+namespace Volo.Abp.Studio
+{
+ [DependsOn(
+ typeof(AbpStudioDomainCommonServicesModule),
+ typeof(AbpStudioModuleInstallerAbstractionsModule)
+ )]
+ public class AbpStudioModuleInstallerModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+
+ }
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/ModuleInstallingPipelineBuilderBase.cs b/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/ModuleInstallingPipelineBuilderBase.cs
new file mode 100644
index 0000000000..8bc8a65304
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/ModuleInstallingPipelineBuilderBase.cs
@@ -0,0 +1,16 @@
+using Volo.Abp.Studio.ModuleInstalling.Steps;
+
+namespace Volo.Abp.Studio.ModuleInstalling
+{
+ public abstract class ModuleInstallingPipelineBuilderBase
+ {
+ protected ModuleInstallingPipeline GetBasePipeline(ModuleInstallingContext context)
+ {
+ var pipeline = new ModuleInstallingPipeline(context);
+
+ pipeline.Steps.Add(new PackageReferencingStep());
+
+ return pipeline;
+ }
+ }
+}
diff --git a/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/Steps/PackageReferencingStep.cs b/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/Steps/PackageReferencingStep.cs
new file mode 100644
index 0000000000..94f97ff9d2
--- /dev/null
+++ b/studio/src/Volo.Abp.Studio.ModuleInstaller/Volo/Abp/Studio/ModuleInstalling/Steps/PackageReferencingStep.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Studio.Packages;
+using Volo.Abp.Studio.Packages.Modifying;
+using Volo.Abp.Studio.Analyzing.Models.Module;
+
+namespace Volo.Abp.Studio.ModuleInstalling.Steps
+{
+ public class PackageReferencingStep : ModuleInstallingPipelineStep
+ {
+ public override async Task ExecuteAsync(ModuleInstallingContext context)
+ {
+ var _csprojFileManager = context.ServiceProvider.GetRequiredService();
+ var _abpModuleFileManager = context.ServiceProvider.GetRequiredService();
+
+ foreach (var referencePackage in context.ReferenceModulePackages)
+ {
+ var targetPackages = GetTargetPackages(context.TargetModulePackages, referencePackage);
+
+ foreach (var targetPackage in targetPackages)
+ {
+ await _csprojFileManager.AddPackageReferenceAsync(
+ targetPackage.Path.RemovePostFix(PackageConsts.FileExtension) + ".csproj",
+ referencePackage.Name,
+ context.Version);
+
+ var targetAbpModulePath = FindAbpModuleFile(targetPackage.Path);
+
+ await _abpModuleFileManager.AddDependency(targetAbpModulePath, FindAbpModuleName(referencePackage));
+ }
+ }
+ }
+
+ private string FindAbpModuleFile(string targetPackagePath)
+ {
+ return Directory.GetFiles(Path.GetDirectoryName(targetPackagePath), "*Module.cs",
+ SearchOption.AllDirectories)
+ .FirstOrDefault();
+ }
+
+ private string FindAbpModuleName(PackageInfoWithAnalyze package)
+ {
+ var abpModuleModel = package.Analyze.Contents.Where(y =>
+ y.ContentType == AbpModuleModel.ContentTypeName
+ ).Cast().First();
+
+ return abpModuleModel.Namespace + "." + abpModuleModel.Name;
+ }
+
+ private List GetTargetPackages(List targetModulePackages,
+ PackageInfoWithAnalyze referencePackage)
+ {
+ if (PackageTypes.IsHostProject(referencePackage.Role))
+ {
+ return new List();
+ }
+
+ if (PackageTypes.IsUiProject(referencePackage.Role))
+ {
+ var useHostBlazorServerForMvcPackages = targetModulePackages.All(p => p.Role != PackageTypes.HostMvc);
+ var targetHostType =
+ PackageTypes.GetHostTypeOfUi(referencePackage.Role, useHostBlazorServerForMvcPackages);
+
+ return targetModulePackages.Where(p => p.Role == targetHostType).ToList();
+ }
+
+ return targetModulePackages.Where(p => p.Role == referencePackage.Role).ToList();
+ }
+ }
+}