feat: upgrade to Angular 22 RC and Nx 22.7.5#2357
Conversation
Bump @angular/* and @angular-devkit/* to ^22.0.0-rc.0, @nx/* to 22.7.5, regenerate the pnpm lockfile, and apply the nx migrate formatting/config updates (nx.json, tsconfig.base.json, plugin builders). Ignore the new .nx/polygraph and .nx/self-healing nx caches. Widen package peer ranges to admit Angular 22 RC and drop the unsupported <17 floor, and fix migration build fallout (nx-plugin and storybook-angular type errors, blog-app sharp trace, docs-app webpack pin). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
✅ Deploy Preview for analog-blog ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for analog-app ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for analog-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
📝 WalkthroughWalkthroughThis PR upgrades the analog monorepo to Angular 22.0.0-rc.2 and TypeScript ~6.0.0, with corresponding NX 22.7.5 and supporting tooling updates. The core workspace dependencies in package.json and all public package peerDependencies are aligned to accept Angular 22 RC. Simultaneously, the NX plugin generators are hardened with explicit non-null assertions and conditional guards throughout project configuration lookups, version parsing, and file I/O operations to enforce stricter TypeScript assumptions. Storybook Angular preset functions receive explicit parameter typing, a new ambient module declaration file resolves deep-import type issues, and Vite configuration is updated to exclude sharp-wasm traces and handle builder metadata more safely. Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
This PR touches multiple package scopes: Please confirm the changes are closely related. |
There was a problem hiding this comment.
Actionable comments posted: 16
🧹 Nitpick comments (5)
packages/nx-plugin/src/generators/app/lib/add-analog-project-config.ts (1)
19-19: ⚖️ Poor tradeoffType safety regression: ProjectConfiguration → any.
This change weakens type safety rather than hardening it, contradicting the PR's stated goal. While the dynamic
[targets]and[builders]keys may complicate typing, losing compile-time checks for the entire configuration object increases the risk of property access errors.Consider using a more precise type or targeted type assertions (e.g.,
as anyonly where needed for dynamic keys) instead of declaring the entire object asany.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/nx-plugin/src/generators/app/lib/add-analog-project-config.ts` at line 19, The projectConfiguration variable was widened to any, losing type safety; restore a precise type by declaring projectConfiguration as ProjectConfiguration (importing the type from `@nrwl/devkit` or nx) and only use targeted casts for truly dynamic keys (e.g., cast projectConfiguration.targets as any when assigning dynamic target names or builders). Keep the overall object typed as ProjectConfiguration and restrict any usage to the minimal expressions that need dynamic indexing (such as (projectConfiguration.targets as any)[dynamicTargetName] = ... or (projectConfiguration.targets as Record<string, any>)[...]) so you retain compile-time checks for all other properties.packages/nx-plugin/src/generators/setup-vitest/generator.ts (1)
51-51: 💤 Low valueRedundant assertion after line 47 already asserted.
Since line 47 already asserts
angularVersion!is non-null, this assertion on line 51 is redundant. If you add validation at line 47 (as suggested above), you can safely remove this assertion.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/nx-plugin/src/generators/setup-vitest/generator.ts` at line 51, The call to addAnalogDependencies(tree, angularVersion!, nxVersion ?? undefined) uses a redundant non-null assertion on angularVersion; remove the trailing "!" and call addAnalogDependencies(tree, angularVersion, nxVersion ?? undefined) after ensuring the prior validation that guarantees angularVersion is non-null (the check/assert at line 47). Update references to the angularVersion symbol accordingly so the function call no longer uses the unnecessary assertion while keeping the existing validation that ensures it's defined.packages/nx-plugin/src/generators/setup-vitest/lib/update-test-target.ts (1)
34-34: 💤 Low valueSimplify redundant condition.
The check
projectConfig && projectConfig?.targetsis redundant. Optional chaining already handles the nullish case, so you can simplify toif (projectConfig?.targets).♻️ Simplified condition
- if (projectConfig && projectConfig?.targets) { + if (projectConfig?.targets) { projectConfig.targets.test = {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/nx-plugin/src/generators/setup-vitest/lib/update-test-target.ts` at line 34, The if-condition redundantly checks projectConfig twice; update the conditional in the update-test-target logic to use optional chaining only (check projectConfig?.targets) so the nullish case is handled once. Locate the conditional that currently reads projectConfig && projectConfig?.targets (referencing projectConfig and its targets property) and replace it with a single if (projectConfig?.targets) check to simplify the guard while preserving behavior.packages/nx-plugin/src/generators/init/lib/update-build-target.ts (1)
70-70: 💤 Low valueRedundant optional chaining in guard condition.
projectConfig && projectConfig?.targetsuses optional chaining (?.) after already confirmingprojectConfigis truthy. Simplify toprojectConfig && projectConfig.targets.♻️ Simplify the guard condition
- if (projectConfig && projectConfig?.targets) { + if (projectConfig && projectConfig.targets) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/nx-plugin/src/generators/init/lib/update-build-target.ts` at line 70, The guard condition in update-build-target.ts uses redundant optional chaining: replace `projectConfig && projectConfig?.targets` with `projectConfig && projectConfig.targets` (or simply `projectConfig?.targets` if preferred) where `projectConfig` is checked before accessing `targets` to remove the unnecessary `?.` and keep the condition clear; update the conditional in the block that references `projectConfig`/`targets` accordingly.tsconfig.base.json (1)
18-18: ⚡ Quick winValid value, but this only defers the
baseUrlcleanup it's masking.
"6.0"is the correct escape-hatch value for TS 6.0. The thing it's silencing here is almost certainlybaseUrl(Line 17): these deprecations can be temporarily suppressed with "ignoreDeprecations": "6.0", but will be removed in TypeScript 7.0. baseUrl: Deprecated. Path mappings no longer require it, and it is no longer treated as a resolution root. Since the ignoreDeprecations: '6.0' escape hatch stops working in 7.0, consider droppingbaseUrlnow (path mappings still resolve without it) to avoid a hard failure when TS 7.0 lands. Fine to defer for this RC PR, but worth a tracking note.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tsconfig.base.json` at line 18, The tsconfig currently sets "ignoreDeprecations": "6.0" which masks the deprecation of "baseUrl"; update tsconfig.base.json by removing the deprecated "baseUrl" entry (referencing the "baseUrl" key) and keep or remove the "ignoreDeprecations" escape hatch as desired—preferably remove "baseUrl" now so path mappings continue to work without it and we won't hit a hard failure in TS 7.0; ensure any path mappings in "paths" still resolve after removing "baseUrl" and add a tracking note if you choose to defer removal.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/test-release.yml:
- Line 98: The Node version is inconsistent across release-validation jobs;
update the node-version value in the create-analog, create-analog-windows, and
create-nx-workspace job definitions to match migrate-angular-app (set
node-version: '24.15.0') or confirm intentional divergence; locate the
node-version entries for the jobs named migrate-angular-app, create-analog,
create-analog-windows, and create-nx-workspace and make the values identical if
the bump should apply workspace-wide.
In `@packages/content/package.json`:
- Around line 26-29: Add a BREAKING CHANGE footer to the squash commit message
that documents the raised peer dependency floor from Angular 15/16 to 17 and
shows the before/after peer ranges for the affected packages; explicitly mention
the changed dependency entries (e.g. "`@angular/core`", "`@angular/common`",
"`@angular/platform-browser`", "`@angular/router`") in packages/content/package.json
and the corresponding entries in packages/router/package.json and
packages/vite-plugin-angular/package.json, and include a short line like
"BREAKING CHANGE: `@analogjs/`* now requires Angular >=17. Before: ... After: ..."
showing the exact previous ranges and the new ranges (including ^22.0.0-rc.0) so
consumers can see the impact.
In `@packages/nx-plugin/src/generators/app/generator.ts`:
- Line 112: The code uses angularVersion returned from
getInstalledPackageVersion without a null check when computing
majorAngularVersion (const majorAngularVersion =
major(coerce(angularVersion!)!)); add a null guard: check if angularVersion is
null/undefined and either supply a safe default string (e.g., '0.0.0' or a
sensible fallback) or throw a clear error before calling coerce/major; update
the flow around getInstalledPackageVersion and the constant majorAngularVersion
so you never use the non-null assertion on angularVersion, referencing the
symbols angularVersion, getInstalledPackageVersion, coerce, major and
majorAngularVersion to locate the change.
In `@packages/nx-plugin/src/generators/app/lib/add-tailwind-helpers.ts`:
- Line 230: Replace the unsafe non-null assertions on project.sourceRoot (used
in the relative(...) calls that set relativeSourceRoot and the other two
occurrences) with a guarded/fallback approach: check if project.sourceRoot is
defined and only call relative(project.root, project.sourceRoot) when it exists,
otherwise use a safe fallback such as relative(project.root, project.root) or an
empty string; update the code paths that build relativeSourceRoot and the other
two places to use project.sourceRoot ?? project.root (or skip computing the
relative path) instead of project.sourceRoot! so runtime errors are avoided when
sourceRoot is undefined.
In `@packages/nx-plugin/src/generators/app/lib/initialize-analog-workspace.ts`:
- Line 43: The call belowMinimumSupportedAngularVersion(angularVersion!) is
unsafe because init* helper functions can still leave angularVersion null;
change the logic in initialize-analog-workspace (the function containing the
init* calls and the belowMinimumSupportedAngularVersion check) to explicitly
handle a null angularVersion after initialization: call
getInstalledPackageVersion('`@angular/core`', ...) into the angularVersion
variable, check if angularVersion is null and then either log a clear error and
abort/return or throw a descriptive error before calling
belowMinimumSupportedAngularVersion, so you never use a non-null assertion on
angularVersion.
In `@packages/nx-plugin/src/generators/app/lib/update-index-html.ts`:
- Line 7: The code uses a non-null assertion on projectConfig
(projectConfig!.root) which can be undefined; update the start of the function
that calls joinPathFragments to explicitly validate the lookup result from
projects.get(projectName) (e.g., const projectConfig =
projects.get(projectName); if (!projectConfig) throw new Error(`Project
'${projectName}' not found`); ) and then use projectConfig.root when calling
joinPathFragments to avoid a runtime crash; reference the projectConfig variable
and the joinPathFragments call in update-index-html.ts.
- Line 11: The code uses a non-null assertion on indexContents when building
updatedIndex (indexContents!.replace(...)), which will throw if tree.read
returned null (e.g., empty files); update the logic in updateIndexHtml to first
call tree.read(...) into indexContents, check for null, and handle it safely
(either treat as empty string or bail with a clear error/log) and convert the
Buffer to a string before calling replace so updatedIndex is only computed from
a valid string.
In
`@packages/nx-plugin/src/generators/app/versions/minimum-supported-versions.ts`:
- Around line 5-8: The helper functions belowMinimumSupportedNxVersion and
belowMinimumSupportedAngularVersion currently use non-null assertions on
semver.coerce() which can be null for malformed inputs; change each to first
assign const parsed = coerce(inputVersion) and if parsed is null throw a clear
Error (e.g., `Invalid version string: "${inputVersion}"`) so callers get a
descriptive failure instead of a runtime crash, then return lt(parsed,
MINIMUM_SUPPORTED_NX_VERSION) / lt(parsed, MINIMUM_SUPPORTED_ANGULAR_VERSION)
respectively; keep references to MINIMUM_SUPPORTED_NX_VERSION and
MINIMUM_SUPPORTED_ANGULAR_VERSION unchanged.
In `@packages/nx-plugin/src/generators/app/versions/nx-dependencies.ts`:
- Line 23: The code currently uses non-null assertions on
semver.clean(nxVersion) (seen as escapedNxVersion) which can be null for
malformed versions; update getNxDependencies to explicitly handle clean(...)
returning null by checking the result and throwing a clear, user-facing error
(e.g., "Invalid nxVersion: '<value>'") instead of using !; apply the same
null-check pattern for the other clean(...) call on line 42 so both cleaned
values are validated and a helpful error is thrown when cleaning fails.
In `@packages/nx-plugin/src/generators/init/generator.ts`:
- Line 65: The chained non-null assertions on major(coerce(angularVersion!)!)
can throw a cryptic TypeError; update the logic around angularVersion and
coercion in generator.ts so you first check that angularVersion is defined and
that coerce(angularVersion) returns a non-null SemVer before calling major.
Locate the angularVersion variable and the const majorAngularVersion assignment,
perform a guard (if missing/invalid) and either throw a clear validation error
(with a helpful message) or handle a safe default, ensuring you reference
coerce(...) and major(...) in the guard so the code never calls major on a null
value.
In `@packages/nx-plugin/src/generators/init/lib/update-index-html.ts`:
- Line 7: The code uses a non-null assertion when retrieving projectConfig:
replace the unsafe projects.get(schema.project)! lookup with a guarded lookup
that validates existence; call projects.get(schema.project) into a local (e.g.,
projectConfig) and if it is undefined, throw a clear error or return early with
a helpful message referencing schema.project; update any subsequent uses of
projectConfig in update-index-html.ts to rely on the validated local variable
(avoid using the '!' operator).
- Line 13: The current code uses a non-null assertion on tree.read(indexPath,
'utf-8') assigned to indexContents which can be null for empty files or read
errors; change this to explicitly check the return value of tree.read(indexPath,
'utf-8') (e.g., const raw = tree.read(...); if (raw === null) { handle it: set
indexContents to '' or throw a clear error }) and then use that guarded value
(indexContents) for subsequent processing in update-index-html.ts so you avoid a
runtime crash when the file is empty or unreadable.
In `@packages/nx-plugin/src/generators/setup-vitest/generator.ts`:
- Line 47: The current double non-null assertion at const majorAngularVersion =
major(coerce(angularVersion!)!); can throw at runtime if `@angular/core` is
missing or the version is unparseable; to fix, validate and handle the value
returned by coerce before calling major: first check angularVersion exists
(e.g., if (!angularVersion) throw new Error("Missing `@angular/core` version -
please install `@angular/core` or run this generator in an Angular workspace")),
then call const coerced = coerce(angularVersion); if (!coerced) throw new
Error("Unable to parse `@angular/core` version: " + String(angularVersion));
finally use const majorAngularVersion = major(coerced); replace the original
line with these guarded checks referencing angularVersion, coerce and major.
- Line 25: The code uses a non-null assertion on projects.get(options.project)
when assigning projectConfig, which can throw if the project key doesn't exist;
update the generator to validate the lookup result first (e.g., const
projectConfig = projects.get(options.project); if (!projectConfig) throw new
Error(`Project "${options.project}" not found in workspace`);) so callers
receive a clear validation error instead of a runtime TypeError; locate the
assignment to projectConfig and replace the non-null assertion with this
existence check referencing options.project and projects.get.
In `@packages/nx-plugin/src/utils/version-utils.ts`:
- Line 23: The code in version-utils.ts uses non-null assertions on
coerce(defaultVersion) and coerce(installedPackageVersion) which can throw if
the version strings are unparseable; update the logic in the function that
returns the cleaned/coerced version (references: defaultVersion,
installedPackageVersion, coerce, clean) to handle null from coerce by adding
explicit fallbacks or error handling: first attempt clean(defaultVersion) and if
that yields null call coerce(defaultVersion) and check for null before using
.version, and do the same for installedPackageVersion, returning a sensible
default (e.g., throw a descriptive error or return a safe fallback string)
instead of using the non-null assertion.
In
`@packages/vite-plugin-angular-tools/src/builders/vite-dev-server/dev-server.impl.ts`:
- Around line 15-16: The code inconsistently handles context.target by using a
non-null assertion in the call to getProjectMetadata (context.target!) and an
optional chain when reading projectName (context.target?.project); fix by adding
a single early guard that checks for presence of context.target and bails with a
clear error if missing, then call getProjectMetadata(context.target) and read
context.target.project without optional chaining—update references in this block
(context.target, getProjectMetadata, projectName, projectConfig) so they assume
a defined target after the guard.
---
Nitpick comments:
In `@packages/nx-plugin/src/generators/app/lib/add-analog-project-config.ts`:
- Line 19: The projectConfiguration variable was widened to any, losing type
safety; restore a precise type by declaring projectConfiguration as
ProjectConfiguration (importing the type from `@nrwl/devkit` or nx) and only use
targeted casts for truly dynamic keys (e.g., cast projectConfiguration.targets
as any when assigning dynamic target names or builders). Keep the overall object
typed as ProjectConfiguration and restrict any usage to the minimal expressions
that need dynamic indexing (such as (projectConfiguration.targets as
any)[dynamicTargetName] = ... or (projectConfiguration.targets as Record<string,
any>)[...]) so you retain compile-time checks for all other properties.
In `@packages/nx-plugin/src/generators/init/lib/update-build-target.ts`:
- Line 70: The guard condition in update-build-target.ts uses redundant optional
chaining: replace `projectConfig && projectConfig?.targets` with `projectConfig
&& projectConfig.targets` (or simply `projectConfig?.targets` if preferred)
where `projectConfig` is checked before accessing `targets` to remove the
unnecessary `?.` and keep the condition clear; update the conditional in the
block that references `projectConfig`/`targets` accordingly.
In `@packages/nx-plugin/src/generators/setup-vitest/generator.ts`:
- Line 51: The call to addAnalogDependencies(tree, angularVersion!, nxVersion ??
undefined) uses a redundant non-null assertion on angularVersion; remove the
trailing "!" and call addAnalogDependencies(tree, angularVersion, nxVersion ??
undefined) after ensuring the prior validation that guarantees angularVersion is
non-null (the check/assert at line 47). Update references to the angularVersion
symbol accordingly so the function call no longer uses the unnecessary assertion
while keeping the existing validation that ensures it's defined.
In `@packages/nx-plugin/src/generators/setup-vitest/lib/update-test-target.ts`:
- Line 34: The if-condition redundantly checks projectConfig twice; update the
conditional in the update-test-target logic to use optional chaining only (check
projectConfig?.targets) so the nullish case is handled once. Locate the
conditional that currently reads projectConfig && projectConfig?.targets
(referencing projectConfig and its targets property) and replace it with a
single if (projectConfig?.targets) check to simplify the guard while preserving
behavior.
In `@tsconfig.base.json`:
- Line 18: The tsconfig currently sets "ignoreDeprecations": "6.0" which masks
the deprecation of "baseUrl"; update tsconfig.base.json by removing the
deprecated "baseUrl" entry (referencing the "baseUrl" key) and keep or remove
the "ignoreDeprecations" escape hatch as desired—preferably remove "baseUrl" now
so path mappings continue to work without it and we won't hit a hard failure in
TS 7.0; ensure any path mappings in "paths" still resolve after removing
"baseUrl" and add a tracking note if you choose to defer removal.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b65a722a-9af4-4ffa-8141-510f31a65d27
⛔ Files ignored due to path filters (3)
.gitignoreis excluded by none and included by none.prettierignoreis excluded by none and included by nonepnpm-lock.yamlis excluded by!**/pnpm-lock.yamland included by none
📒 Files selected for processing (37)
.github/workflows/test-release.ymlapps/blog-app/vite.config.tsnx.jsonpackage.jsonpackages/astro-angular/package.jsonpackages/content/package.jsonpackages/nx-plugin/src/generators/app/generator.tspackages/nx-plugin/src/generators/app/lib/add-analog-project-config.tspackages/nx-plugin/src/generators/app/lib/add-tailwind-helpers.tspackages/nx-plugin/src/generators/app/lib/initialize-analog-workspace.tspackages/nx-plugin/src/generators/app/lib/update-index-html.tspackages/nx-plugin/src/generators/app/versions/minimum-supported-versions.tspackages/nx-plugin/src/generators/app/versions/nx-dependencies.tspackages/nx-plugin/src/generators/init/generator.tspackages/nx-plugin/src/generators/init/lib/add-analog-dependencies.tspackages/nx-plugin/src/generators/init/lib/update-app-tsconfig.tspackages/nx-plugin/src/generators/init/lib/update-build-target.tspackages/nx-plugin/src/generators/init/lib/update-git-ignore.tspackages/nx-plugin/src/generators/init/lib/update-index-html.tspackages/nx-plugin/src/generators/init/lib/update-main.tspackages/nx-plugin/src/generators/init/lib/update-package-json.tspackages/nx-plugin/src/generators/init/lib/update-serve-target.tspackages/nx-plugin/src/generators/init/lib/update-test-target.tspackages/nx-plugin/src/generators/init/lib/update-test-tsconfig.tspackages/nx-plugin/src/generators/setup-vitest/generator.tspackages/nx-plugin/src/generators/setup-vitest/lib/add-analog-dependencies.tspackages/nx-plugin/src/generators/setup-vitest/lib/update-test-target.tspackages/nx-plugin/src/generators/setup-vitest/lib/update-tsconfig.tspackages/nx-plugin/src/utils/version-utils.tspackages/router/package.jsonpackages/storybook-angular/src/lib/preset.tspackages/storybook-angular/src/storybook-angular-subpaths.d.tspackages/vite-plugin-angular-tools/src/builders/vite-dev-server/dev-server.impl.tspackages/vite-plugin-angular-tools/src/builders/vite/vite-build.impl.tspackages/vite-plugin-angular/package.jsonpackages/vitest-angular/package.jsontsconfig.base.json
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '24.11.0' | ||
| node-version: '24.15.0' |
There was a problem hiding this comment.
Node version now drifts across the release-validation jobs.
Only migrate-angular-app moves to 24.15.0; create-analog (Line 42), create-analog-windows (Line 61), and create-nx-workspace (Line 78) still pin 24.11.0. If the bump was needed for the Angular 22 migration path, the other release-test jobs are now validating on a different Node than the one this release actually targets. Please confirm the divergence is intentional, or align all four jobs.
🔧 Align the remaining jobs
- node-version: '24.11.0'
+ node-version: '24.15.0'(apply at Lines 42, 61, and 78 if the bump should be workspace-wide)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/test-release.yml at line 98, The Node version is
inconsistent across release-validation jobs; update the node-version value in
the create-analog, create-analog-windows, and create-nx-workspace job
definitions to match migrate-angular-app (set node-version: '24.15.0') or
confirm intentional divergence; locate the node-version entries for the jobs
named migrate-angular-app, create-analog, create-analog-windows, and
create-nx-workspace and make the values identical if the bump should apply
workspace-wide.
|
|
||
| const angularVersion = getInstalledPackageVersion(tree, '@angular/core'); | ||
| const majorAngularVersion = major(coerce(angularVersion)); | ||
| const majorAngularVersion = major(coerce(angularVersion!)!); |
There was a problem hiding this comment.
Unsafe non-null assertion on potentially-null angularVersion.
getInstalledPackageVersion returns string | null (line 111), but line 112 immediately asserts angularVersion! without a null check. If @angular/core is not found in package.json and no default version is provided, this will throw a runtime TypeError.
🛡️ Add null guard before deriving major version
const angularVersion = getInstalledPackageVersion(tree, '`@angular/core`');
+ if (!angularVersion) {
+ throw new Error('Unable to determine Angular version. Please ensure `@angular/core` is installed.');
+ }
const majorAngularVersion = major(coerce(angularVersion)!);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const majorAngularVersion = major(coerce(angularVersion!)!); | |
| const angularVersion = getInstalledPackageVersion(tree, '`@angular/core`'); | |
| if (!angularVersion) { | |
| throw new Error('Unable to determine Angular version. Please ensure `@angular/core` is installed.'); | |
| } | |
| const majorAngularVersion = major(coerce(angularVersion)!); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/nx-plugin/src/generators/app/generator.ts` at line 112, The code
uses angularVersion returned from getInstalledPackageVersion without a null
check when computing majorAngularVersion (const majorAngularVersion =
major(coerce(angularVersion!)!)); add a null guard: check if angularVersion is
null/undefined and either supply a safe default string (e.g., '0.0.0' or a
sensible fallback) or throw a clear error before calling coerce/major; update
the flow around getInstalledPackageVersion and the constant majorAngularVersion
so you never use the non-null assertion on angularVersion, referencing the
symbols angularVersion, getInstalledPackageVersion, coerce, major and
majorAngularVersion to locate the change.
| project.root, | ||
| { | ||
| relativeSourceRoot: relative(project.root, project.sourceRoot), | ||
| relativeSourceRoot: relative(project.root, project.sourceRoot!), |
There was a problem hiding this comment.
Unsafe non-null assertion on project.sourceRoot.
ProjectConfiguration.sourceRoot is string | undefined, but the non-null assertion project.sourceRoot! on lines 230, 242, and 255 assumes it's always defined. If sourceRoot is undefined, relative(project.root, undefined) will throw at runtime.
🛡️ Add guard or fallback for undefined sourceRoot
generateFiles(
tree,
joinPathFragments(__dirname, '..', 'files', 'tailwind/v2'),
project.root,
{
- relativeSourceRoot: relative(project.root, project.sourceRoot!),
+ relativeSourceRoot: relative(project.root, project.sourceRoot ?? project.root),
template: '',
},
);Apply the same pattern to lines 242 and 255.
Also applies to: 242-242, 255-255
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/nx-plugin/src/generators/app/lib/add-tailwind-helpers.ts` at line
230, Replace the unsafe non-null assertions on project.sourceRoot (used in the
relative(...) calls that set relativeSourceRoot and the other two occurrences)
with a guarded/fallback approach: check if project.sourceRoot is defined and
only call relative(project.root, project.sourceRoot) when it exists, otherwise
use a safe fallback such as relative(project.root, project.root) or an empty
string; update the code paths that build relativeSourceRoot and the other two
places to use project.sourceRoot ?? project.root (or skip computing the relative
path) instead of project.sourceRoot! so runtime errors are avoided when
sourceRoot is undefined.
| } | ||
|
|
||
| if (belowMinimumSupportedAngularVersion(angularVersion)) { | ||
| if (belowMinimumSupportedAngularVersion(angularVersion!)) { |
There was a problem hiding this comment.
Unsafe non-null assertion on angularVersion after initialization.
While the initialization logic (lines 23-40) attempts to install Angular if missing, the init* functions return getInstalledPackageVersion(tree, '@angular/core', undefined, true), which can still return null if the package isn't found in package.json. The non-null assertion angularVersion! on line 43 will crash if the initialization doesn't successfully add the package to package.json.
🛡️ Add null check after initialization
}
}
+ if (!angularVersion) {
+ throw new Error('Failed to initialize Angular. Unable to determine Angular version.');
+ }
+
if (belowMinimumSupportedAngularVersion(angularVersion)) {
throw new Error(
stripIndents`Analog only supports an Angular version of 15 and higher`,
);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (belowMinimumSupportedAngularVersion(angularVersion!)) { | |
| } | |
| } | |
| if (!angularVersion) { | |
| throw new Error('Failed to initialize Angular. Unable to determine Angular version.'); | |
| } | |
| if (belowMinimumSupportedAngularVersion(angularVersion)) { | |
| throw new Error( | |
| stripIndents`Analog only supports an Angular version of 15 and higher`, | |
| ); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/nx-plugin/src/generators/app/lib/initialize-analog-workspace.ts` at
line 43, The call belowMinimumSupportedAngularVersion(angularVersion!) is unsafe
because init* helper functions can still leave angularVersion null; change the
logic in initialize-analog-workspace (the function containing the init* calls
and the belowMinimumSupportedAngularVersion check) to explicitly handle a null
angularVersion after initialization: call
getInstalledPackageVersion('`@angular/core`', ...) into the angularVersion
variable, check if angularVersion is null and then either log a clear error and
abort/return or throw a descriptive error before calling
belowMinimumSupportedAngularVersion, so you never use a non-null assertion on
angularVersion.
|
|
||
| if (tree.exists(indexPath)) { | ||
| const indexContents = tree.read(indexPath, 'utf-8'); | ||
| const indexContents = tree.read(indexPath, 'utf-8')!; |
There was a problem hiding this comment.
Non-null assertion on tree.read may fail for empty files.
While tree.exists(indexPath) confirms the file exists, tree.read() can still return null for empty files or read errors. The non-null assertion risks a runtime failure.
🛡️ Proposed guard to handle missing content
if (tree.exists(indexPath)) {
- const indexContents = tree.read(indexPath, 'utf-8')!;
+ const indexContents = tree.read(indexPath, 'utf-8');
+ if (!indexContents) {
+ return;
+ }
let updatedIndex = indexContents.replace(📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const indexContents = tree.read(indexPath, 'utf-8')!; | |
| if (tree.exists(indexPath)) { | |
| const indexContents = tree.read(indexPath, 'utf-8'); | |
| if (!indexContents) { | |
| return; | |
| } | |
| let updatedIndex = indexContents.replace( |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/nx-plugin/src/generators/init/lib/update-index-html.ts` at line 13,
The current code uses a non-null assertion on tree.read(indexPath, 'utf-8')
assigned to indexContents which can be null for empty files or read errors;
change this to explicitly check the return value of tree.read(indexPath,
'utf-8') (e.g., const raw = tree.read(...); if (raw === null) { handle it: set
indexContents to '' or throw a clear error }) and then use that guarded value
(indexContents) for subsequent processing in update-index-html.ts so you avoid a
runtime crash when the file is empty or unreadable.
| const isNx = tree.exists('/nx.json'); | ||
|
|
||
| const projectConfig = projects.get(options.project); | ||
| const projectConfig = projects.get(options.project)!; |
There was a problem hiding this comment.
Runtime risk: non-null assertion assumes project exists.
If options.project refers to a non-existent project in the workspace, this assertion will throw a confusing TypeError rather than a clear validation error. Consider validating the project exists or providing a helpful error message.
🛡️ Defensive alternative
- const projectConfig = projects.get(options.project)!;
+ const projectConfig = projects.get(options.project);
+ if (!projectConfig) {
+ throw new Error(`Project "${options.project}" not found in workspace`);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const projectConfig = projects.get(options.project)!; | |
| const projectConfig = projects.get(options.project); | |
| if (!projectConfig) { | |
| throw new Error(`Project "${options.project}" not found in workspace`); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/nx-plugin/src/generators/setup-vitest/generator.ts` at line 25, The
code uses a non-null assertion on projects.get(options.project) when assigning
projectConfig, which can throw if the project key doesn't exist; update the
generator to validate the lookup result first (e.g., const projectConfig =
projects.get(options.project); if (!projectConfig) throw new Error(`Project
"${options.project}" not found in workspace`);) so callers receive a clear
validation error instead of a runtime TypeError; locate the assignment to
projectConfig and replace the non-null assertion with this existence check
referencing options.project and projects.get.
| ) { | ||
| const angularVersion = getInstalledPackageVersion(tree, '@angular/core'); | ||
| const majorAngularVersion = major(coerce(angularVersion)); | ||
| const majorAngularVersion = major(coerce(angularVersion!)!); |
There was a problem hiding this comment.
Runtime risk: double non-null assertion on Angular version.
If @angular/core is not installed or has an unparseable version, this will throw at runtime. For a generator that configures Vitest for Angular projects, validating Angular's presence upfront would provide clearer error messages.
🛡️ Defensive alternative
const angularVersion = getInstalledPackageVersion(tree, '`@angular/core`');
- const majorAngularVersion = major(coerce(angularVersion!)!);
+ if (!angularVersion) {
+ throw new Error('`@angular/core` must be installed to set up Vitest');
+ }
+ const coercedVersion = coerce(angularVersion);
+ if (!coercedVersion) {
+ throw new Error(`Invalid `@angular/core` version: ${angularVersion}`);
+ }
+ const majorAngularVersion = major(coercedVersion);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/nx-plugin/src/generators/setup-vitest/generator.ts` at line 47, The
current double non-null assertion at const majorAngularVersion =
major(coerce(angularVersion!)!); can throw at runtime if `@angular/core` is
missing or the version is unparseable; to fix, validate and handle the value
returned by coerce before calling major: first check angularVersion exists
(e.g., if (!angularVersion) throw new Error("Missing `@angular/core` version -
please install `@angular/core` or run this generator in an Angular workspace")),
then call const coerced = coerce(angularVersion); if (!coerced) throw new
Error("Unable to parse `@angular/core` version: " + String(angularVersion));
finally use const majorAngularVersion = major(coerced); replace the original
line with these guarded checks referencing angularVersion, coerce and major.
| installedPackageVersion === 'next' | ||
| ) { | ||
| return clean(defaultVersion) ?? coerce(defaultVersion).version; | ||
| return clean(defaultVersion!) ?? coerce(defaultVersion)!.version; |
There was a problem hiding this comment.
Non-null assertions on coerce results assume version strings are always valid.
While the control flow ensures defaultVersion exists at line 23 (due to the guard at lines 14-16), both coerce(defaultVersion)! (line 23) and coerce(installedPackageVersion)! (line 28) assume the version strings are always parseable. Malformed version strings in package.json dependencies will cause runtime crashes.
Consider handling null returns from coerce with explicit fallbacks or error messages.
🛡️ Add fallback for unparseable versions
if (
!installedPackageVersion ||
installedPackageVersion === 'latest' ||
installedPackageVersion === 'next'
) {
- return clean(defaultVersion!) ?? coerce(defaultVersion)!.version;
+ const cleaned = clean(defaultVersion!);
+ if (cleaned) return cleaned;
+ const coerced = coerce(defaultVersion!);
+ if (!coerced) {
+ throw new Error(`Unable to parse version for ${packageName}: "${defaultVersion}"`);
+ }
+ return coerced.version;
}
return (
(raw ? installedPackageVersion : clean(installedPackageVersion)) ??
- coerce(installedPackageVersion)!.version
+ (coerce(installedPackageVersion)?.version ?? installedPackageVersion)
);Also applies to: 28-28
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/nx-plugin/src/utils/version-utils.ts` at line 23, The code in
version-utils.ts uses non-null assertions on coerce(defaultVersion) and
coerce(installedPackageVersion) which can throw if the version strings are
unparseable; update the logic in the function that returns the cleaned/coerced
version (references: defaultVersion, installedPackageVersion, coerce, clean) to
handle null from coerce by adding explicit fallbacks or error handling: first
attempt clean(defaultVersion) and if that yields null call
coerce(defaultVersion) and check for null before using .version, and do the same
for installedPackageVersion, returning a sensible default (e.g., throw a
descriptive error or return a safe fallback string) instead of using the
non-null assertion.
| const projectConfig = await context.getProjectMetadata(context.target!); | ||
| const projectName = context.target?.project; |
There was a problem hiding this comment.
Contradictory nullability handling on context.target.
Line 15 asserts context.target! is defined while line 16 guards it with context.target?.. Pick one stance: if the target can legitimately be undefined, the ! on line 15 just forwards undefined into getProjectMetadata and surfaces an opaque failure; if it can't, the ?. is dead weight. A single early guard keeps both lines honest.
🛡️ Proposed guard
- const projectConfig = await context.getProjectMetadata(context.target!);
- const projectName = context.target?.project;
+ if (!context.target) {
+ throw new Error('vite-dev-server builder requires a target.');
+ }
+ const projectConfig = await context.getProjectMetadata(context.target);
+ const projectName = context.target.project;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const projectConfig = await context.getProjectMetadata(context.target!); | |
| const projectName = context.target?.project; | |
| if (!context.target) { | |
| throw new Error('vite-dev-server builder requires a target.'); | |
| } | |
| const projectConfig = await context.getProjectMetadata(context.target); | |
| const projectName = context.target.project; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@packages/vite-plugin-angular-tools/src/builders/vite-dev-server/dev-server.impl.ts`
around lines 15 - 16, The code inconsistently handles context.target by using a
non-null assertion in the call to getProjectMetadata (context.target!) and an
optional chain when reading projectName (context.target?.project); fix by adding
a single early guard that checks for presence of context.target and bails with a
clear error if missing, then call getProjectMetadata(context.target) and read
context.target.project without optional chaining—update references in this block
(context.target, getProjectMetadata, projectName, projectConfig) so they assume
a defined target after the guard.
PR Checklist
Upgrades the workspace to Angular 22 RC and Nx 22.7.5, regenerates the lockfile, applies the
nx migrateconfig/formatting updates, and fixes the build/test fallout from the bump. Also widens package peer ranges to admit Angular 22 RC and drops the unsupported<17floor.Closes #
Affected scope
nx.json,tsconfig.base.json, rootpackage.json,pnpm-lock.yaml)Recommended merge strategy for maintainer [optional]
What is the new behavior?
@angular/*and@angular-devkit/*to^22.0.0-rc.0,@nx/*to22.7.5, and regenerates the pnpm lockfile.nx migrateupdates (nx.json,tsconfig.base.json, plugin builders) and ignores the new.nx/polygraphand.nx/self-healingcaches.<17floor:astro-angular,content,router,vite-plugin-angular(@angular/build+@angular-devkit/build-angular), andvitest-angular(@angular-devkit/architect+@angular-devkit/schematics).@nx/*peer ranges left untouched.projectConfigpossibly-undefined guards, nullablecoerce/clean/version-util returns,tree.readnull, narrowed index/deletetypes).@storybook/angularsubpath exports that no longer ship.d.ts, and types the now-untyped preset-hook params.@img/sharp-wasm32optional symlink in nitro's external file trace (was throwingENOENTonrealpath).webpackto5.105.4so Docusaurus'webpackbarProgressPlugin options pass webpack's schema validation (5.106 tightened it).Test plan
nx format:checkpnpm buildpnpm testpnpm buildis green for all 20 projects;pnpm testis green for all 18 projects. Builds and tests were run locally on darwin-arm64.Does this PR introduce a breaking change?
Drops support for Angular versions below 17 in the published peer ranges (
astro-angular,content,router,vite-plugin-angular,vitest-angular). Consumers on Angular 15/16 should remain on the previous Analog release. Angular 17–21 and 22 RC are supported.Other information
The branch is a single squashable commit on top of
beta.🤖 Generated with Claude Code