Repository navigation
Conversation
Compute the workingDirectory smart default relative to the active project root so ng generate from a multi-project application directory uses src/app defaults instead of the project folder itself. Fixes angular#21660
There was a problem hiding this comment.
Code Review
This pull request introduces a new utility function, getSchematicWorkingDirectory, to resolve the workingDirectory smart default for schematics relative to the project root. The reviewer identified potential issues where the resolved path could escape the project or workspace root and provided code suggestions to guard against these cases by returning undefined. Additionally, the reviewer recommended adding a unit test to verify the behavior when the current working directory is outside the project root.
| const projectRelative = devkitNormalize(path.relative(projectRoot, cwd)); | ||
|
|
||
| return projectRelative === '' ? undefined : projectRelative; |
There was a problem hiding this comment.
In a single-project workspace where the project root is not the workspace root (e.g., projects/app), if the command is run from the workspace root, workspace.projects.size === 1 will eagerly select the single project. This causes path.relative(projectRoot, cwd) to resolve to a path escaping the project root (e.g., ../..). Returning this relative path will cause schematics to generate files outside the project root (at the workspace root).
To prevent this, we should check if the resolved relative path starts with .. or is absolute, and return undefined in those cases so that schematics fall back to their default paths.
const projectRelative = devkitNormalize(path.relative(projectRoot, cwd));
if (projectRelative.startsWith('..') || path.isAbsolute(projectRelative)) {
return undefined;
}
return projectRelative === '' ? undefined : projectRelative;| it('returns undefined for a single-project workspace when cwd is the workspace root', () => { | ||
| const workspace = mockWorkspace({ | ||
| app: { root: '' }, | ||
| }); | ||
|
|
||
| expect(getSchematicWorkingDirectory(workspace, workspaceRoot)).toBeUndefined(); | ||
| }); |
There was a problem hiding this comment.
Add a unit test to verify that getSchematicWorkingDirectory returns undefined when the single project has a non-empty root and the command is run from the workspace root (outside the project root).
it('returns undefined for a single-project workspace when cwd is the workspace root', () => {
const workspace = mockWorkspace({
app: { root: '' },
});
expect(getSchematicWorkingDirectory(workspace, workspaceRoot)).toBeUndefined();
});
it('returns undefined for a single-project workspace when cwd is outside the project root', () => {
const workspace = mockWorkspace({
app: { root: 'projects/app' },
});
expect(getSchematicWorkingDirectory(workspace, workspaceRoot)).toBeUndefined();
});| const workspaceRelative = devkitNormalize(path.relative(workspace.basePath, cwd)); | ||
|
|
||
| return workspaceRelative === '' ? undefined : workspaceRelative; |
There was a problem hiding this comment.
Similarly, if cwd is outside the workspace base path, workspaceRelative could start with .. or be absolute. We should guard against this to ensure we only return paths within the workspace.
const workspaceRelative = devkitNormalize(path.relative(workspace.basePath, cwd));
if (workspaceRelative.startsWith('..') || path.isAbsolute(workspaceRelative)) {
return undefined;
}
return workspaceRelative === '' ? undefined : workspaceRelative;|
Thank you for the pull request. There are two issues with this change:
Closing this pull request for now, as any change here will need a broader design review. |
PR Checklist
PR Type
What is the current behavior?
Issue Number: #21660
In a multi-project workspace,
ng generatefrom a project root (e.g.projects/app1) uses the workspace-relative working directory as the schematicpathdefault. Files are created under the project folder instead ofsrc/app/src/lib.What is the new behavior?
The
workingDirectorysmart default is resolved relative to the active project root. From the project root, schematics receive no working-directory override and apply their usual default path (src/apporsrc/lib). From deeper folders inside the project, paths remain relative to the project tree.Does this PR introduce a breaking change?
Other information
Fixes #21660
Tests:
pnpm bazel test //packages/angular/cli:test --test_filter="getSchematicWorkingDirectory"Made with Cursor