Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,34 @@ internal async Task<BlazorIdentityModel> ValidateAndBuild(BlazorIdentityCommandL
}

blazorIdentityModel.BaseOutputPath = Path.Combine(AppInfo.ApplicationBasePath, commandlineModel.RelativeFolderPath);
blazorIdentityModel.RootNamespace = ProjectContext.RootNamespace;

// For WASM/Auto Global Blazor projects, Identity Account files belong in the client project,
// not the server project. In server/server-global projects, MainLayout.razor lives at
// Components/Layout/MainLayout.razor. Its absence indicates a WASM/Auto Global setup where
// the client project holds all interactive components.
var mainLayoutInServerProject = Path.Combine(AppInfo.ApplicationBasePath, "Components", "Layout", "MainLayout.razor");
if (!FileSystem.FileExists(mainLayoutInServerProject))
{
var clientProjectRef = ProjectContext.ProjectReferenceInformation
?.FirstOrDefault(p =>
(p.AssemblyName?.EndsWith(".Client", StringComparison.OrdinalIgnoreCase) == true) ||
(p.ProjectName?.EndsWith(".Client", StringComparison.OrdinalIgnoreCase) == true));
if (clientProjectRef != null)
{
var clientProjectPath = Path.GetDirectoryName(clientProjectRef.FullPath);
if (!string.IsNullOrEmpty(clientProjectPath))
{
var clientRootNamespace = clientProjectRef.AssemblyName
?? Path.GetFileNameWithoutExtension(clientProjectRef.FullPath);
blazorIdentityModel.BaseOutputPath = Path.Combine(clientProjectPath, commandlineModel.RelativeFolderPath);
blazorIdentityModel.RootNamespace = clientRootNamespace;
blazorIdentityModel.BlazorIdentityNamespace = $"{clientRootNamespace}.Components.Account";
blazorIdentityModel.BlazorLayoutNamespace = $"{clientRootNamespace}.Layout.MainLayout";
}
}
}

return blazorIdentityModel;
}

Expand Down Expand Up @@ -474,7 +502,7 @@ private void ExecuteTemplates(BlazorIdentityModel templateModel)
string extension = templateName.StartsWith("Pages", StringComparison.OrdinalIgnoreCase) ||
templateName.StartsWith("Shared", StringComparison.OrdinalIgnoreCase) ? ".razor" : ".cs";
string templateNameWithNamespace = $"{templateModel.BlazorIdentityNamespace}.{templateName}";
string templatePath = StringUtil.ToPath(templateNameWithNamespace, templateModel.BaseOutputPath, ProjectContext.RootNamespace);
string templatePath = StringUtil.ToPath(templateNameWithNamespace, templateModel.BaseOutputPath, templateModel.RootNamespace ?? ProjectContext.RootNamespace);
string templatedFilePath = $"{templatePath}{extension}";
var folderName = Path.GetDirectoryName(templatedFilePath);
if (!FileSystem.DirectoryExists(folderName))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@ public BlazorIdentityModel()
public DbProvider DatabaseProvider { get; set; }
public List<string> FilesToGenerate { get; set; }
public string BaseOutputPath { get; set; }
// The root namespace used for computing output file paths. Defaults to the server project's
// root namespace, but overridden to the client project's namespace for WASM/Auto Global projects.
public string RootNamespace { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ public static IScaffoldBuilder WithBlazorIdentityStaticFilesStep(this IScaffoldB

step.ProjectPath = projectPath;

if (context.Properties.TryGetValue(nameof(IdentityModel), out var identityModelObj) &&
identityModelObj is IdentityModel identityModel &&
!string.IsNullOrEmpty(identityModel.BaseOutputPath) &&
Directory.Exists(identityModel.BaseOutputPath))
{
step.BaseOutputDirectory = Path.Combine(identityModel.BaseOutputPath, "Components", "Account", "Shared");
step.FileName = "PasskeySubmit.razor.js";
return;
}

if (context.Properties.TryGetValue(nameof(IdentitySettings), out var commandSettingsObj) && commandSettingsObj is IdentitySettings commandSettings)
{
var projectDirectory = Path.GetDirectoryName(commandSettings.Project);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ internal static IEnumerable<TextTemplatingProperty> GetTextTemplatingProperties(
x.FullName.Contains(templateFullName) &&
x.Name.Equals(typeName, StringComparison.OrdinalIgnoreCase));

var projectName = Path.GetFileNameWithoutExtension(blazorIdentityModel.ProjectInfo.ProjectPath);
var projectName = blazorIdentityModel.IdentityProjectName
?? Path.GetFileNameWithoutExtension(blazorIdentityModel.ProjectInfo.ProjectPath);

if (!string.IsNullOrEmpty(templatePath) && templateType is not null && !string.IsNullOrEmpty(projectName))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ internal class IdentityModel
/// </summary>
public required string BaseOutputPath { get; set; }
/// <summary>
/// Gets or sets the project name used for identity file namespace and path resolution.
/// For WASM/Auto Global projects this is the client project name; otherwise the server project name.
/// </summary>
public string? IdentityProjectName { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to overwrite existing files.
/// </summary>
public bool Overwrite { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,32 @@ public override async Task<bool> ExecuteAsync(ScaffolderContext context, Cancell
userClassNamespace = $"{projectName}.Data";
}

// For WASM/Auto Global Blazor projects, Identity Account files belong in the client project.
// In server/server-global projects, MainLayout.razor lives at Components/Layout/MainLayout.razor.
// Its absence indicates a WASM/Auto Global setup where the client project holds all interactive components.
string baseOutputPath = projectDirectory;
string? identityProjectName = null;
if (settings.BlazorScenario && !string.IsNullOrEmpty(projectName))
{
var mainLayoutInServerProject = Path.Combine(projectDirectory, "Components", "Layout", "MainLayout.razor");
if (!_fileSystem.FileExists(mainLayoutInServerProject))
{
var parentDirectory = Path.GetDirectoryName(projectDirectory);
if (!string.IsNullOrEmpty(parentDirectory))
{
var clientProjectFolderName = projectName + ".Client";
var clientProjectDirectory = Path.Combine(parentDirectory, clientProjectFolderName);
if (_fileSystem.DirectoryExists(clientProjectDirectory))
{
baseOutputPath = clientProjectDirectory;
identityProjectName = clientProjectFolderName;
identityNamespace = $"{clientProjectFolderName}.Components.Account";
identityLayoutNamespace = $"{clientProjectFolderName}.Layout.MainLayout";
}
}
}
}

IdentityModel scaffoldingModel = new()
{
ProjectInfo = projectInfo,
Expand All @@ -225,7 +251,8 @@ public override async Task<bool> ExecuteAsync(ScaffolderContext context, Cancell
UserClassName = AspNetConstants.Identity.UserClassName,
UserClassNamespace = userClassNamespace,
IdentityLayoutNamespace = identityLayoutNamespace,
BaseOutputPath = projectDirectory,
BaseOutputPath = baseOutputPath,
IdentityProjectName = identityProjectName,
Overwrite = settings.Overwrite
};

Expand Down