Upgrade Angular version to 20#918
Conversation
Migrate workspace to Angular 19.2.9, Nx 20.8.4, TypeScript 5.7.3, and related dependencies. Adapt to Angular 19 standalone-by-default by adding explicit imports to test components and standalone:false to playground components. Replace deprecated APP_INITIALIZER with provideAppInitializer in persist-lang, preload-langs, and testing module. Fix locale test mocks to use explicit en-US locale. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
… config Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Migrate workspace to Angular 20.3.9, Nx 21.6.9, TypeScript 5.8.3, and related dependencies. Fix moduleResolution for Node.js libraries incompatible with bundler mode. Disable @angular-eslint/prefer-inject rule (constructor injection refactoring deferred to separate PR). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
📝 WalkthroughWalkthroughMajor version upgrades of Angular (18→20), Nx (19→21), and TypeScript (5.5→5.9) alongside widespread ESLint configuration changes disabling Changes
Fun Fact: In i18n, the term "localization" is sometimes abbreviated as "L10n" (the letter L, 10 letters, and then n), just like "internationalization" becomes "i18n" (i, 18 letters, n). The number-letter-number pattern is called a numeronym! 🌍 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
libs/transloco-persist-lang/tsconfig.lib.json (1)
5-11:⚠️ Potential issue | 🟡 MinorMinor:
libtarget mismatch —es2018vstarget: "es2020".The compiler
targetises2020(Line 5) butlibis still["dom", "es2018"](Line 10). Every other updated library tsconfig in this PR uses["dom", "es2020"](e.g.,libs/transloco-preload-langs/tsconfig.lib.json). Suggest aligning for consistency and to expose the full ES2020 lib typings (e.g.,Promise.allSettled,BigInt,globalThis).🔧 Proposed alignment
- "lib": ["dom", "es2018"], + "lib": ["dom", "es2020"], "moduleResolution": "bundler"🌐 i18n tidbit: "lang" in Swedish means "long" — coincidentally how long this
es2018has likely been lingering. 😄🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/transloco-persist-lang/tsconfig.lib.json` around lines 5 - 11, Update the tsconfig to align library typings with the compilation target: change the "lib" array in tsconfig.lib.json from ["dom", "es2018"] to ["dom", "es2020"] so it matches "target": "es2020"; modify the "lib" entry (near the existing "target" and "moduleResolution" entries) to ensure ES2020 runtime types like Promise.allSettled, BigInt, and globalThis are available during compilation.libs/transloco-preload-langs/src/lib/preload-langs.providers.ts (1)
38-39:⚠️ Potential issue | 🟡 MinorRemove dead
noopfunction and its eslint-disable comment.The
noophelper is no longer used following the switch toprovideAppInitializer(() => { inject(...) }).🧹 Proposed cleanup
-// eslint-disable-next-line `@typescript-eslint/no-empty-function` -function noop() {} - export function provideTranslocoPreloadLangs(langs: string[]) {In many languages, small grammatical particles serve similar purposes — like French's "ne" in negative constructions — invisible threads holding meaning together. Similarly,
nooponce held a purpose here; now it's become as unnecessary as a silent letter.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/transloco-preload-langs/src/lib/preload-langs.providers.ts` around lines 38 - 39, Remove the now-unused noop helper and its eslint-disable comment: delete the noop function declaration (function noop() {}) and the preceding "// eslint-disable-next-line `@typescript-eslint/no-empty-function`" line so there is no dead code or unnecessary linter override left in preload-langs.providers.ts; ensure no other references to noop remain in the file (e.g., any calls or exports) and run the linter to confirm no new warnings.
🧹 Nitpick comments (12)
.gitignore (1)
65-67: Anchor these ignore rules to repo root for safer matching.If these are intended as top-level paths only, prefix with
/(Line 65–67) to avoid unintentionally ignoring similarly named paths in nested folders.Suggested tweak
-investigations -.cursor/rules/nx-rules.mdc -.github/instructions/nx.instructions.md +/investigations +/.cursor/rules/nx-rules.mdc +/.github/instructions/nx.instructions.mdFun i18n fact: “Hello” in Hebrew is שלום (shalom).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.gitignore around lines 65 - 67, The .gitignore entries "investigations", ".cursor/rules/nx-rules.mdc", and ".github/instructions/nx.instructions.md" are unanchored and may match nested paths; make them top-level-only by prefixing each with a leading slash (e.g. "/investigations", "/.cursor/rules/nx-rules.mdc", "/.github/instructions/nx.instructions.md") so they only ignore those paths at the repository root.libs/transloco-messageformat/tsconfig.lib.json (1)
10-11: LGTM —moduleResolution: "bundler"fits Angular 20 + TS 5.8.Consistent with the repo-wide alignment. One tiny nit worth noting (non-blocking):
lib: ["dom", "es2018"]here is a bit behind thetarget: "es2020"and thees2020/ES2021libs used in sibling packages — you may want to bump it toes2020in a follow-up for consistency, though it won't cause issues today.🦊 Fun fact: ICU MessageFormat, which powers this package, was originally developed at IBM in the 1990s as part of the ICU (International Components for Unicode) project — one of the earliest widely-adopted attempts to standardize pluralization and gender rules across ~700 locales.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/transloco-messageformat/tsconfig.lib.json` around lines 10 - 11, Summary: Bump the TypeScript libs to match project target by replacing "es2018" with "es2020". Edit tsconfig.lib.json and update the "lib" array entry "es2018" to "es2020" (so it becomes ["dom","es2020"]) to align with the repo's target/other packages; leave "moduleResolution": "bundler" unchanged. Ensure no other tsconfig overrides revert this change.libs/transloco-persist-lang/src/lib/persist-lang.providers.ts (1)
18-19: Dead code: remove the now-unusednoophelper.With the migration to
provideAppInitializer, thenoopfactory is no longer referenced anywhere in this file. The eslint-disable comment can go with it.🧹 Proposed cleanup
-// eslint-disable-next-line `@typescript-eslint/no-empty-function` -function noop() {} - type providerValue =🌍 Fun i18n fact: In Japanese, "引き継ぎ" (hikitsugi) means "handover" or "takeover" — fitting for this API migration from
APP_INITIALIZERtoprovideAppInitializer.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/transloco-persist-lang/src/lib/persist-lang.providers.ts` around lines 18 - 19, Remove the dead helper by deleting the noop function and its preceding eslint-disable comment in this file: locate the noop declaration (function noop() {}) and remove both the function and the line "// eslint-disable-next-line `@typescript-eslint/no-empty-function`" since noop is no longer referenced after migrating to provideAppInitializer..eslintrc.json (1)
39-39: Consider tracking the deferredprefer-injectrefactor.Per the PR description the constructor-injection refactor was intentionally deferred, but the root config now silences
@angular-eslint/prefer-injecteverywhere (plus each project-level.eslintrc.jsonduplicates the disable). A JSON config can't host a// TODO, but it's worth opening a tracking issue so this rule can be re-enabled once services are migrated toinject(). Also, with the root override in place, the per-library disables become redundant — you could consolidate.Want me to open a follow-up issue to re-enable
@angular-eslint/prefer-injectafter migrating services toinject()?🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.eslintrc.json at line 39, The root ESLint config currently disables the rule "@angular-eslint/prefer-inject" globally and duplicates that disable across project-level .eslintrc.json files; create a follow-up tracking issue to re-enable "@angular-eslint/prefer-inject" after migrating services to inject(), remove the global "off" override from the root .eslintrc.json so the rule can be re-enabled later (or set it to "warn" if you prefer a softer rollout), and then consolidate by removing redundant per-library disables so the rule is controlled centrally via the tracking issue plan.package.json (1)
48-48: Minor: version specifier inconsistency for@angular/cli.
@angular/cliuses~20.3.0while the rest of the@angular/*packages (includingcompiler-cliandlanguage-service) are pinned exactly to20.3.9. Consider aligning them (pin to20.3.9or loosen all@angular/*entries to~20.3.0) to keep the CLI in lockstep with the runtime it tools against.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@package.json` at line 48, Update the `@angular/cli` version specifier to match the rest of the Angular packages: change "@angular/cli": "~20.3.0" to the exact pinned version used elsewhere (e.g., "20.3.9") or alternatively loosen the other `@angular/`* entries to "~20.3.0" so all `@angular` packages use the same versioning style; modify the package.json entry for "@angular/cli" to the chosen consistent specifier and run npm/yarn install to refresh lockfiles.libs/transloco-preload-langs/.eslintrc.json (1)
12-12: Technical debt: Constructor injection pattern retained.Disabling
@angular-eslint/prefer-injectdefers migration to Angular's moderninject()function. While this is a pragmatic choice for the current upgrade, consider planning a follow-up task to adopt the inject pattern, as it offers better tree-shaking, improved testability, and aligns with Angular 20+ idioms.📚 i18n wisdom: In Korean, "defer" is "연기하다" (yeon-gi-ha-da). Interestingly, many programming concepts about timing and sequencing translate quite literally across languages, showing how universal these computational concepts are!
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/transloco-preload-langs/.eslintrc.json` at line 12, The ESLint rule "@angular-eslint/prefer-inject" has been turned off which preserves constructor injection; restore the rule and create a follow-up migration to replace constructor-based DI with Angular's inject() where appropriate (e.g., services, components, directives) by updating providers to call inject(MyService) instead of receiving instances via constructors, adjusting unit tests to use inject() or TestBed.inject, and adding a tracking task in your backlog to perform this sweep across symbols that currently use constructor injection; re-enable "@angular-eslint/prefer-inject" in the .eslintrc.json once the migration plan is in place.libs/transloco-messageformat/.eslintrc.json (1)
12-12: Technical debt: Modern injection pattern deferred.Similar to other libraries, disabling
@angular-eslint/prefer-injectmaintains constructor injection. While reasonable for this upgrade cycle, migrating toinject()should be considered for future iterations.💬 MessageFormat magic: The ICU MessageFormat standard (which this library wraps) was designed to handle pluralization across languages. Did you know that some languages like Arabic have 6 plural forms, while Japanese has just 1? MessageFormat handles this elegance automatically!
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/transloco-messageformat/.eslintrc.json` at line 12, Re-enable the Angular lint rule by changing the "@angular-eslint/prefer-inject" setting from "off" back to "error" in the ESLint config so the codebase is warned to migrate from constructor-based injection to the modern inject() pattern; locate the rule entry named "@angular-eslint/prefer-inject" in the .eslintrc.json and update its value to "error" (you can run autofix/lint to see violations and plan migrating constructors to inject() later).libs/transloco/.eslintrc.json (1)
13-13: Technical debt acknowledged in core library.The core Transloco library also defers the inject() migration. Given the library's size and public API surface, this is a sensible approach for the v18→v20 upgrade. A coordinated migration across all Transloco packages would ensure API consistency.
🎯 Translation milestone: Transloco itself crossed a major milestone by upgrading through 2 major Angular versions at once! In the i18n world, this is like translating between 3 related but distinct languages — you need to understand the evolution and maintain meaning across all versions. 翻訳おめでとうございます (Honyaku omedetō gozaimasu - Congratulations on the translation, in Japanese)!
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/transloco/.eslintrc.json` at line 13, The config currently disables the "@angular-eslint/prefer-inject" rule in the core Transloco ESLint config; create a short, actionable migration plan and surface it alongside the config: convert .eslintrc.json to a comment-friendly format (e.g., .eslintrc.cjs), add a concise TODO comment explaining that "@angular-eslint/prefer-inject" is intentionally set to "off" and reference a newly created migration issue/PR number that tracks the coordinated v18→v20 inject() migration across Transloco packages, and ensure the plan notes which packages will be migrated together (so later re-enabling the rule can be done consistently).libs/transloco-scoped-libs/tsconfig.spec.json (1)
3-8: Consider whetherbundlerwould be more consistent with sibling spec configs.Most spec tsconfigs in this PR moved to
"bundler";transloco-scoped-libs(andtransloco-optimize) stayed on"node". This is fine when paired withmodule: "commonjs", but if these specs don't actually depend on CJS-style resolution semantics, aligning them with the majority would reduce the cognitive load of "why is this one different?" for future maintainers. Non-blocking.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/transloco-scoped-libs/tsconfig.spec.json` around lines 3 - 8, Update the tsconfig spec so its module resolution matches the other spec configs: change the "moduleResolution" property from "node" to "bundler" in the tsconfig JSON (leave "module": "commonjs" as-is unless tests require otherwise), then run the spec tests to confirm no resolution regressions; the keys to edit are "moduleResolution" and optionally check "module" in the tsconfig.spec.json.libs/transloco-locale/.eslintrc.json (1)
12-12: Prefer-inject disabled — tracked as deferred refactor.Disabling
@angular-eslint/prefer-injectfor.tsand.htmlis consistent with the rest of the workspace and matches the PR's stated intent to defer theinject()migration. Consider opening a follow-up issue to re-enable this rule once constructor injection is migrated, so it doesn't silently stay off forever.🌐 i18n fun: "locale" comes from Latin locālis ("of a place"). Ironically, this file disables a rule about where dependencies come from. 📍
Also applies to: 34-36
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/transloco-locale/.eslintrc.json` at line 12, The rule "@angular-eslint/prefer-inject" is turned off in the ESLint config; leave it disabled for now to match the PR intent but create a follow-up task/issue to re-enable "@angular-eslint/prefer-inject" once constructor-to-inject() migration is completed (note the same change applies at the other occurrences around lines 34-36), and ensure the issue references the config entry "@angular-eslint/prefer-inject" so it isn't forgotten.libs/transloco-locale/src/lib/tests/mocks.ts (1)
54-58: Behavior change:langChanges$now always emits.Previously
langChanges$could be emitted without a value whenlocalewas absent; now it always emits'en-US'by default. This is backward compatible with call sites likelibs/transloco-locale/src/lib/tests/utils.ts:40which invoke it with no args, and actually makes the mock more realistic (the realTranslocoService.langChanges$always emits a lang string). 👍🈶 Minor nit: since
'en-US'is already exported asDEFAULT_LOCALE_MOCKon line 11, you could reuse it to keep the single source of truth:♻️ Optional refactor
-export function provideTranslocoServiceMock(locale: Locale = 'en-US') { +export function provideTranslocoServiceMock(locale: Locale = DEFAULT_LOCALE_MOCK) { return mockProvider(TranslocoService, { langChanges$: of(locale), }); }🗺️ Fun fact: the BCP 47 tag
en-UShas a cousin —en-US-POSIX— a real, registered locale used by software that wants "plain" English sorting rules.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/transloco-locale/src/lib/tests/mocks.ts` around lines 54 - 58, The mock provider now always emits a locale; update provideTranslocoServiceMock to reuse the existing DEFAULT_LOCALE_MOCK constant instead of hardcoding 'en-US' so the single source of truth is preserved: change the default parameter and the of(...) emission in provideTranslocoServiceMock to use DEFAULT_LOCALE_MOCK (referencing the DEFAULT_LOCALE_MOCK symbol and the provideTranslocoServiceMock function).apps/transloco-playground/.eslintrc.json (1)
12-12: Add tracking for the deferredinject()migration and remove the no-op rule from the HTML override.Disabling
@angular-eslint/prefer-injectin the TypeScript rules is reasonable as a temporary measure (aligning with the deferred constructor-injection refactor), but without an associated tracking issue, this easily becomes permanent tech debt. Consider adding a// TODOcomment linked to a follow-up issue.Additionally,
@angular-eslint/prefer-injectin the HTML override (line 34) is a no-op—the rule targets TypeScript class declarations only, not templates. Remove it from the HTML rules block:♻️ Proposed cleanup
{ "files": ["*.html"], - "extends": ["plugin:`@nx/angular-template`"], - "rules": { - "@angular-eslint/prefer-inject": "off" - } + "extends": ["plugin:`@nx/angular-template`"], + "rules": {} }Fun fact: In Spanish, "inyectar" (to inject) is also used in dependency injection contexts—Angular's
inject()function would literally be "inyectar()" in Spanish-speaking dev communities!🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/transloco-playground/.eslintrc.json` at line 12, Add a TODO comment next to the disabled rule "@angular-eslint/prefer-inject" in the TypeScript rules block noting a tracking issue/PR for the deferred inject() migration (e.g., "// TODO: track deferred inject() migration - ISSUE-1234") and then remove the "@angular-eslint/prefer-inject" entry from the HTML override rules block (the no-op rule in the overrides that targets templates). Update only the .eslintrc.json entries: leave the TypeScript disable but annotate it with the TODO and delete the HTML override occurrence of "@angular-eslint/prefer-inject".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@libs/transloco-messageformat/.eslintrc.json`:
- Around line 35-37: Remove the misplaced "@angular-eslint/prefer-inject" rule
from the HTML/template override in the .eslintrc.json because that rule targets
TypeScript constructors/classes, not template files; locate the HTML override
block containing the "rules" object and delete the
"@angular-eslint/prefer-inject": "off" entry so the template linter uses only
template-specific rules from `@angular-eslint/eslint-plugin-template`.
In `@libs/transloco-utils/src/lib/transloco-utils.types.d.ts`:
- Around line 1-24: The committed hand-generated declaration file
transloco-utils.types.d.ts (containing the TranslocoGlobalConfig and nested
keysManager types) is a build artifact and should be removed; delete this .d.ts
(and the other generated .d.ts files transloco-utils.d.ts and index.d.ts) from
src/, add a .d.ts exclusion to the repository ignore rules so
TypeScript-generated declaration files are not re-committed, and rely on the
real TypeScript sources (e.g. the TranslocoGlobalConfig/interface in your .ts
files) to produce declarations into dist/ during the build.
---
Outside diff comments:
In `@libs/transloco-persist-lang/tsconfig.lib.json`:
- Around line 5-11: Update the tsconfig to align library typings with the
compilation target: change the "lib" array in tsconfig.lib.json from ["dom",
"es2018"] to ["dom", "es2020"] so it matches "target": "es2020"; modify the
"lib" entry (near the existing "target" and "moduleResolution" entries) to
ensure ES2020 runtime types like Promise.allSettled, BigInt, and globalThis are
available during compilation.
In `@libs/transloco-preload-langs/src/lib/preload-langs.providers.ts`:
- Around line 38-39: Remove the now-unused noop helper and its eslint-disable
comment: delete the noop function declaration (function noop() {}) and the
preceding "// eslint-disable-next-line `@typescript-eslint/no-empty-function`"
line so there is no dead code or unnecessary linter override left in
preload-langs.providers.ts; ensure no other references to noop remain in the
file (e.g., any calls or exports) and run the linter to confirm no new warnings.
---
Nitpick comments:
In @.eslintrc.json:
- Line 39: The root ESLint config currently disables the rule
"@angular-eslint/prefer-inject" globally and duplicates that disable across
project-level .eslintrc.json files; create a follow-up tracking issue to
re-enable "@angular-eslint/prefer-inject" after migrating services to inject(),
remove the global "off" override from the root .eslintrc.json so the rule can be
re-enabled later (or set it to "warn" if you prefer a softer rollout), and then
consolidate by removing redundant per-library disables so the rule is controlled
centrally via the tracking issue plan.
In @.gitignore:
- Around line 65-67: The .gitignore entries "investigations",
".cursor/rules/nx-rules.mdc", and ".github/instructions/nx.instructions.md" are
unanchored and may match nested paths; make them top-level-only by prefixing
each with a leading slash (e.g. "/investigations",
"/.cursor/rules/nx-rules.mdc", "/.github/instructions/nx.instructions.md") so
they only ignore those paths at the repository root.
In `@apps/transloco-playground/.eslintrc.json`:
- Line 12: Add a TODO comment next to the disabled rule
"@angular-eslint/prefer-inject" in the TypeScript rules block noting a tracking
issue/PR for the deferred inject() migration (e.g., "// TODO: track deferred
inject() migration - ISSUE-1234") and then remove the
"@angular-eslint/prefer-inject" entry from the HTML override rules block (the
no-op rule in the overrides that targets templates). Update only the
.eslintrc.json entries: leave the TypeScript disable but annotate it with the
TODO and delete the HTML override occurrence of "@angular-eslint/prefer-inject".
In `@libs/transloco-locale/.eslintrc.json`:
- Line 12: The rule "@angular-eslint/prefer-inject" is turned off in the ESLint
config; leave it disabled for now to match the PR intent but create a follow-up
task/issue to re-enable "@angular-eslint/prefer-inject" once
constructor-to-inject() migration is completed (note the same change applies at
the other occurrences around lines 34-36), and ensure the issue references the
config entry "@angular-eslint/prefer-inject" so it isn't forgotten.
In `@libs/transloco-locale/src/lib/tests/mocks.ts`:
- Around line 54-58: The mock provider now always emits a locale; update
provideTranslocoServiceMock to reuse the existing DEFAULT_LOCALE_MOCK constant
instead of hardcoding 'en-US' so the single source of truth is preserved: change
the default parameter and the of(...) emission in provideTranslocoServiceMock to
use DEFAULT_LOCALE_MOCK (referencing the DEFAULT_LOCALE_MOCK symbol and the
provideTranslocoServiceMock function).
In `@libs/transloco-messageformat/.eslintrc.json`:
- Line 12: Re-enable the Angular lint rule by changing the
"@angular-eslint/prefer-inject" setting from "off" back to "error" in the ESLint
config so the codebase is warned to migrate from constructor-based injection to
the modern inject() pattern; locate the rule entry named
"@angular-eslint/prefer-inject" in the .eslintrc.json and update its value to
"error" (you can run autofix/lint to see violations and plan migrating
constructors to inject() later).
In `@libs/transloco-messageformat/tsconfig.lib.json`:
- Around line 10-11: Summary: Bump the TypeScript libs to match project target
by replacing "es2018" with "es2020". Edit tsconfig.lib.json and update the "lib"
array entry "es2018" to "es2020" (so it becomes ["dom","es2020"]) to align with
the repo's target/other packages; leave "moduleResolution": "bundler" unchanged.
Ensure no other tsconfig overrides revert this change.
In `@libs/transloco-persist-lang/src/lib/persist-lang.providers.ts`:
- Around line 18-19: Remove the dead helper by deleting the noop function and
its preceding eslint-disable comment in this file: locate the noop declaration
(function noop() {}) and remove both the function and the line "//
eslint-disable-next-line `@typescript-eslint/no-empty-function`" since noop is no
longer referenced after migrating to provideAppInitializer.
In `@libs/transloco-preload-langs/.eslintrc.json`:
- Line 12: The ESLint rule "@angular-eslint/prefer-inject" has been turned off
which preserves constructor injection; restore the rule and create a follow-up
migration to replace constructor-based DI with Angular's inject() where
appropriate (e.g., services, components, directives) by updating providers to
call inject(MyService) instead of receiving instances via constructors,
adjusting unit tests to use inject() or TestBed.inject, and adding a tracking
task in your backlog to perform this sweep across symbols that currently use
constructor injection; re-enable "@angular-eslint/prefer-inject" in the
.eslintrc.json once the migration plan is in place.
In `@libs/transloco-scoped-libs/tsconfig.spec.json`:
- Around line 3-8: Update the tsconfig spec so its module resolution matches the
other spec configs: change the "moduleResolution" property from "node" to
"bundler" in the tsconfig JSON (leave "module": "commonjs" as-is unless tests
require otherwise), then run the spec tests to confirm no resolution
regressions; the keys to edit are "moduleResolution" and optionally check
"module" in the tsconfig.spec.json.
In `@libs/transloco/.eslintrc.json`:
- Line 13: The config currently disables the "@angular-eslint/prefer-inject"
rule in the core Transloco ESLint config; create a short, actionable migration
plan and surface it alongside the config: convert .eslintrc.json to a
comment-friendly format (e.g., .eslintrc.cjs), add a concise TODO comment
explaining that "@angular-eslint/prefer-inject" is intentionally set to "off"
and reference a newly created migration issue/PR number that tracks the
coordinated v18→v20 inject() migration across Transloco packages, and ensure the
plan notes which packages will be migrated together (so later re-enabling the
rule can be done consistently).
In `@package.json`:
- Line 48: Update the `@angular/cli` version specifier to match the rest of the
Angular packages: change "@angular/cli": "~20.3.0" to the exact pinned version
used elsewhere (e.g., "20.3.9") or alternatively loosen the other `@angular/`*
entries to "~20.3.0" so all `@angular` packages use the same versioning style;
modify the package.json entry for "@angular/cli" to the chosen consistent
specifier and run npm/yarn install to refresh lockfiles.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 88c62146-9e3b-4da9-af0d-b5efbc55373e
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (72)
.eslintrc.json.gitignoreapps/transloco-playground/.eslintrc.jsonapps/transloco-playground/project.jsonapps/transloco-playground/src/app/app.component.tsapps/transloco-playground/src/app/dynamic-translation/dynamic-translation.component.tsapps/transloco-playground/src/app/home/home.component.tsapps/transloco-playground/src/app/inline-loaders/inline-loaders.component.tsapps/transloco-playground/src/app/lazy-multiple-scopes/lazy-multiple-scopes.component.tsapps/transloco-playground/src/app/lazy-scope-alias/lazy-scope-alias.component.tsapps/transloco-playground/src/app/lazy/lazy.component.tsapps/transloco-playground/src/app/locale/locale.component.tsapps/transloco-playground/src/app/multi-langs/multi-langs.component.tsapps/transloco-playground/src/app/multi-langs/provider-lang/provider-lang.component.tsapps/transloco-playground/src/app/scope-sharing/scope-sharing.component.tsapps/transloco-playground/src/app/transpilers/transpilers.component.tsapps/transloco-playground/tsconfig.app.jsonapps/transloco-playground/tsconfig.spec.jsonjest.config.tslibs/transloco-locale/.eslintrc.jsonlibs/transloco-locale/project.jsonlibs/transloco-locale/src/lib/tests/mocks.tslibs/transloco-locale/tsconfig.lib.jsonlibs/transloco-locale/tsconfig.lib.prod.jsonlibs/transloco-locale/tsconfig.spec.jsonlibs/transloco-messageformat/.eslintrc.jsonlibs/transloco-messageformat/project.jsonlibs/transloco-messageformat/tsconfig.lib.jsonlibs/transloco-messageformat/tsconfig.lib.prod.jsonlibs/transloco-messageformat/tsconfig.spec.jsonlibs/transloco-optimize/tsconfig.lib.jsonlibs/transloco-optimize/tsconfig.spec.jsonlibs/transloco-persist-lang/.eslintrc.jsonlibs/transloco-persist-lang/project.jsonlibs/transloco-persist-lang/src/lib/persist-lang.providers.tslibs/transloco-persist-lang/tsconfig.lib.jsonlibs/transloco-persist-lang/tsconfig.lib.prod.jsonlibs/transloco-persist-lang/tsconfig.spec.jsonlibs/transloco-persist-translations/.eslintrc.jsonlibs/transloco-persist-translations/project.jsonlibs/transloco-persist-translations/tsconfig.lib.jsonlibs/transloco-persist-translations/tsconfig.lib.prod.jsonlibs/transloco-persist-translations/tsconfig.spec.jsonlibs/transloco-preload-langs/.eslintrc.jsonlibs/transloco-preload-langs/project.jsonlibs/transloco-preload-langs/src/lib/preload-langs.providers.tslibs/transloco-preload-langs/tsconfig.lib.jsonlibs/transloco-preload-langs/tsconfig.lib.prod.jsonlibs/transloco-preload-langs/tsconfig.spec.jsonlibs/transloco-schematics/project.jsonlibs/transloco-scoped-libs/tsconfig.lib.jsonlibs/transloco-scoped-libs/tsconfig.spec.jsonlibs/transloco-utils/src/index.d.tslibs/transloco-utils/src/lib/transloco-utils.d.tslibs/transloco-utils/src/lib/transloco-utils.types.d.tslibs/transloco-utils/tsconfig.lib.jsonlibs/transloco-utils/tsconfig.spec.jsonlibs/transloco-validator/tsconfig.lib.jsonlibs/transloco-validator/tsconfig.spec.jsonlibs/transloco/.eslintrc.jsonlibs/transloco/project.jsonlibs/transloco/src/lib/tests/directive/multi-scope-alias.spec.tslibs/transloco/src/lib/tests/directive/scope-alias.spec.tslibs/transloco/src/lib/tests/inline-loaders.spec.tslibs/transloco/src/lib/tests/pipe/pipe-integration.spec.tslibs/transloco/src/lib/tests/signal.spec.tslibs/transloco/src/lib/transloco-testing.module.tslibs/transloco/tsconfig.lib.jsonlibs/transloco/tsconfig.lib.prod.jsonlibs/transloco/tsconfig.spec.jsonnx.jsonpackage.json
💤 Files with no reviewable changes (13)
- apps/transloco-playground/src/app/lazy/lazy.component.ts
- apps/transloco-playground/src/app/dynamic-translation/dynamic-translation.component.ts
- apps/transloco-playground/src/app/home/home.component.ts
- apps/transloco-playground/src/app/multi-langs/multi-langs.component.ts
- libs/transloco/src/lib/tests/signal.spec.ts
- apps/transloco-playground/src/app/locale/locale.component.ts
- apps/transloco-playground/src/app/multi-langs/provider-lang/provider-lang.component.ts
- apps/transloco-playground/src/app/inline-loaders/inline-loaders.component.ts
- apps/transloco-playground/src/app/scope-sharing/scope-sharing.component.ts
- apps/transloco-playground/src/app/lazy-scope-alias/lazy-scope-alias.component.ts
- apps/transloco-playground/src/app/transpilers/transpilers.component.ts
- apps/transloco-playground/src/app/app.component.ts
- apps/transloco-playground/src/app/lazy-multiple-scopes/lazy-multiple-scopes.component.ts
| "rules": { | ||
| "@angular-eslint/prefer-inject": "off" | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check if `@angular-eslint/prefer-inject` rule has HTML template support
# Search for other instances of prefer-inject in HTML overrides
echo "=== Checking other .eslintrc.json files for HTML prefer-inject overrides ==="
rg -n "files.*html" -A 5 .eslintrc.json | rg -A 3 "prefer-inject"
# Check if this pattern appears elsewhere in the codebase
echo -e "\n=== Count of HTML overrides with prefer-inject ==="
rg -l "files.*\[.*\.html.*\]" --glob "*.eslintrc.json" | xargs rg -l "prefer-inject"Repository: jsverse/transloco
Length of output: 479
🏁 Script executed:
# First, let's view the specific file mentioned
cat -n libs/transloco-messageformat/.eslintrc.jsonRepository: jsverse/transloco
Length of output: 1306
🏁 Script executed:
# Check a couple of other files to see if they have the same pattern
echo "=== Checking libs/transloco/.eslintrc.json ==="
cat -n libs/transloco/.eslintrc.json | grep -A 5 -B 5 "\.html"
echo -e "\n=== Checking apps/transloco-playground/.eslintrc.json ==="
cat -n apps/transloco-playground/.eslintrc.json | grep -A 5 -B 5 "\.html"
echo -e "\n=== Search for prefer-inject in context of HTML files ==="
rg "files.*html" -A 8 .eslintrc.json | grep -E "(files|prefer-inject)"Repository: jsverse/transloco
Length of output: 727
🏁 Script executed:
# Check if there's any configuration or documentation about prefer-inject
echo "=== Search for prefer-inject mentions in project ==="
rg "prefer-inject" --type json --type md
# Check git history for this specific file if available
echo -e "\n=== Check recent changes to this file ==="
git log --oneline -5 -- libs/transloco-messageformat/.eslintrc.json 2>/dev/null || echo "Git history not available"Repository: jsverse/transloco
Length of output: 1351
🌐 Web query:
@angular-eslint/prefer-inject rule HTML templates scope
💡 Result:
The @angular-eslint/prefer-inject rule applies only to TypeScript files (.ts), specifically checking Angular class decorators like @Component, @Injectable, @Directive, etc., and their constructors or field initializers for constructor parameter injection vs. inject usage. It does not apply to HTML templates. HTML templates are linted by the separate @angular-eslint/eslint-plugin-template package, which uses @angular-eslint/template-parser. This plugin has its own set of rules focused on template syntax, bindings, attributes, accessibility, etc. (e.g., no-call-expression, prefer-control-flow), but prefer-inject is not among them, as dependency injection via inject does not occur in templates—templates use property/event bindings, pipes, and directives instead.
Citations:
- 1: https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/docs/rules/prefer-inject.md
- 2: https://www.npmjs.com/package/@angular-eslint/eslint-plugin-template
- 3: https://www.npmjs.com/package/@angular-eslint/eslint-plugin
Remove prefer-inject rule from HTML override section — the rule applies only to TypeScript files.
The @angular-eslint/prefer-inject rule is designed exclusively for TypeScript classes and constructors, not HTML templates. Since HTML templates are linted by the separate @angular-eslint/eslint-plugin-template package with its own rule set, this rule in the HTML override (lines 35-37) is redundant. This pattern appears across multiple .eslintrc.json files in the project, likely defensive configuration, but it should be removed for clarity.
Fun fact: The inject() function you're linting for is part of Angular's standalone API, which aligns well with i18n patterns like Transloco — both modernize how developers declare and manage dependencies in Angular! 🌍
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@libs/transloco-messageformat/.eslintrc.json` around lines 35 - 37, Remove the
misplaced "@angular-eslint/prefer-inject" rule from the HTML/template override
in the .eslintrc.json because that rule targets TypeScript constructors/classes,
not template files; locate the HTML override block containing the "rules" object
and delete the "@angular-eslint/prefer-inject": "off" entry so the template
linter uses only template-specific rules from
`@angular-eslint/eslint-plugin-template`.
| export interface TranslocoGlobalConfig { | ||
| rootTranslationsPath?: string; | ||
| defaultLang?: string; | ||
| scopedLibs?: | ||
| | string[] | ||
| | Array<{ | ||
| src: string; | ||
| dist: string[]; | ||
| }>; | ||
| scopePathMap?: Record<string, string>; | ||
| langs?: string[]; | ||
| keysManager?: { | ||
| input?: string | string[]; | ||
| output?: string; | ||
| fileFormat?: 'json' | 'pot'; | ||
| marker?: string; | ||
| addMissingKeys?: boolean; | ||
| emitErrorOnExtraKeys?: boolean; | ||
| replace?: boolean; | ||
| defaultValue?: string | undefined; | ||
| unflat?: boolean; | ||
| sort?: boolean; | ||
| }; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm that a .ts counterpart exists for each committed .d.ts in transloco-utils/src,
# and check whether these .d.ts files are listed in tsconfig "files"/"include" or gitignore.
fd -e d.ts . libs/transloco-utils/src
echo '---'
fd -e ts -E '*.d.ts' . libs/transloco-utils/src
echo '--- gitignore / tsconfig refs ---'
rg -n --hidden -g '!node_modules' '\.d\.ts' libs/transloco-utils .gitignore 2>/dev/null || trueRepository: jsverse/transloco
Length of output: 501
🏁 Script executed:
#!/bin/bash
# Compare .d.ts files with their .ts counterparts to check if they're identical
echo "=== Comparing transloco-utils.types.d.ts vs transloco-utils.types.ts ==="
diff libs/transloco-utils/src/lib/transloco-utils.types.d.ts libs/transloco-utils/src/lib/transloco-utils.types.ts
echo ""
echo "=== Comparing transloco-utils.d.ts vs transloco-utils.ts ==="
diff libs/transloco-utils/src/lib/transloco-utils.d.ts libs/transloco-utils/src/lib/transloco-utils.ts
echo ""
echo "=== Comparing index.d.ts vs index.ts ==="
diff libs/transloco-utils/src/index.d.ts libs/transloco-utils/src/index.tsRepository: jsverse/transloco
Length of output: 944
🏁 Script executed:
#!/bin/bash
# Check primary tsconfig and build settings
echo "=== tsconfig.json settings ==="
cat libs/transloco-utils/tsconfig.json | grep -A 5 '"declaration"'
echo ""
echo "=== tsconfig.lib.json if exists ==="
if [ -f libs/transloco-utils/tsconfig.lib.json ]; then cat libs/transloco-utils/tsconfig.lib.json | grep -A 5 '"declaration"'; else echo "No tsconfig.lib.json"; fi
echo ""
echo "=== Root tsconfig compilerOptions for declarations ==="
head -100 tsconfig.base.json | grep -A 2 -B 2 'declaration'Repository: jsverse/transloco
Length of output: 454
🏁 Script executed:
#!/bin/bash
# Check project.json for build configuration
echo "=== project.json build target ==="
if [ -f libs/transloco-utils/project.json ]; then cat libs/transloco-utils/project.json | grep -A 20 '"build"'; else echo "No project.json"; fiRepository: jsverse/transloco
Length of output: 811
🏁 Script executed:
#!/bin/bash
# Check if .d.ts files are in .gitignore and where types should be published from
echo "=== .gitignore content (check for .d.ts) ==="
cat .gitignore | grep -i "\.d\.ts" || echo "No .d.ts pattern found in .gitignore"
echo ""
echo "=== package.json typings reference ==="
cat package.json | grep -A 2 -B 2 '"types"'
echo ""
echo "=== libs/transloco-utils package.json if exists ==="
if [ -f libs/transloco-utils/package.json ]; then cat libs/transloco-utils/package.json | grep -A 3 '"types"'; else echo "No package.json in libs/transloco-utils"; fiRepository: jsverse/transloco
Length of output: 235
🏁 Script executed:
#!/bin/bash
# Check what gets published/included in distribution
echo "=== tsconfig.lib.json declaration options ==="
cat libs/transloco-utils/tsconfig.lib.json | grep -A 5 '"declaration\|declarationDir\|declarationMap"'Repository: jsverse/transloco
Length of output: 240
Remove hand-maintained .d.ts files from src/ — these are build artifacts.
The three .d.ts files (transloco-utils.types.d.ts, transloco-utils.d.ts, and index.d.ts) are auto-generated by TypeScript during the build (tsconfig.lib.json has declaration: true). They should not be committed to the repository:
transloco-utils.d.tsis a pure declaration file with no implementation—classic auto-generation patterntransloco-utils.types.d.tsshows formatting drift (your.tsuses single-line unions, the.d.tsuses multi-line wrapping), confirming they're falling out of sync with manual edits- No
.gitignorerule prevents their re-introduction after the next build
These should exist only in dist/libs/transloco-utils/ post-build, not in src/. Remove these three files and add a .d.ts exclusion to .gitignore to prevent accidental re-commits.
🌍 Language tidbit: In German, the word for "translate" is übersetzen, literally "to set over"—which perfectly captures what a .d.ts does: it sets type information over compiled JavaScript.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@libs/transloco-utils/src/lib/transloco-utils.types.d.ts` around lines 1 - 24,
The committed hand-generated declaration file transloco-utils.types.d.ts
(containing the TranslocoGlobalConfig and nested keysManager types) is a build
artifact and should be removed; delete this .d.ts (and the other generated .d.ts
files transloco-utils.d.ts and index.d.ts) from src/, add a .d.ts exclusion to
the repository ignore rules so TypeScript-generated declaration files are not
re-committed, and rely on the real TypeScript sources (e.g. the
TranslocoGlobalConfig/interface in your .ts files) to produce declarations into
dist/ during the build.
There was a problem hiding this comment.
3 issues found across 73 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="libs/transloco/src/lib/tests/inline-loaders.spec.ts">
<violation number="1" location="libs/transloco/src/lib/tests/inline-loaders.spec.ts:68">
P1: `imports` was added to a non-standalone component, which Angular treats as invalid component metadata and can break compilation of this spec.</violation>
</file>
<file name="libs/transloco/src/lib/tests/directive/multi-scope-alias.spec.ts">
<violation number="1" location="libs/transloco/src/lib/tests/directive/multi-scope-alias.spec.ts:59">
P2: `TestPipe` uses `imports` without being standalone, which will break Angular compilation for this spec component.</violation>
</file>
<file name="libs/transloco-utils/src/lib/transloco-utils.types.d.ts">
<violation number="1" location="libs/transloco-utils/src/lib/transloco-utils.types.d.ts:1">
P2: These `.d.ts` files appear to be build artifacts — `tsconfig.lib.json` already has `"declaration": true`, so they are auto-generated during build. Committing them to `src/` means they can silently drift from the actual `.ts` sources. Remove these three files (`index.d.ts`, `transloco-utils.d.ts`, `transloco-utils.types.d.ts`) and add a `.gitignore` rule to prevent re-introduction.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| }); | ||
|
|
||
| @Component({ | ||
| imports: [TranslocoPipe], |
There was a problem hiding this comment.
P1: imports was added to a non-standalone component, which Angular treats as invalid component metadata and can break compilation of this spec.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At libs/transloco/src/lib/tests/inline-loaders.spec.ts, line 68:
<comment>`imports` was added to a non-standalone component, which Angular treats as invalid component metadata and can break compilation of this spec.</comment>
<file context>
@@ -64,6 +65,7 @@ describe('Inline loaders: directive', () => {
});
@Component({
+ imports: [TranslocoPipe],
template: `
<span>{{ 'todos.title' | transloco }}</span>
</file context>
| imports: [TranslocoPipe], | |
| standalone: true, | |
| imports: [TranslocoPipe], |
| }); | ||
|
|
||
| @Component({ | ||
| imports: [TranslocoPipe], |
There was a problem hiding this comment.
P2: TestPipe uses imports without being standalone, which will break Angular compilation for this spec component.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At libs/transloco/src/lib/tests/directive/multi-scope-alias.spec.ts, line 59:
<comment>`TestPipe` uses `imports` without being standalone, which will break Angular compilation for this spec component.</comment>
<file context>
@@ -55,6 +56,7 @@ describe('Scope alias', () => {
});
@Component({
+ imports: [TranslocoPipe],
template: `
<p>{{ 'lazy.title' | transloco }}</p>
</file context>
| imports: [TranslocoPipe], | |
| + standalone: true, | |
| + imports: [TranslocoPipe], |
| @@ -0,0 +1,24 @@ | |||
| export interface TranslocoGlobalConfig { | |||
There was a problem hiding this comment.
P2: These .d.ts files appear to be build artifacts — tsconfig.lib.json already has "declaration": true, so they are auto-generated during build. Committing them to src/ means they can silently drift from the actual .ts sources. Remove these three files (index.d.ts, transloco-utils.d.ts, transloco-utils.types.d.ts) and add a .gitignore rule to prevent re-introduction.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At libs/transloco-utils/src/lib/transloco-utils.types.d.ts, line 1:
<comment>These `.d.ts` files appear to be build artifacts — `tsconfig.lib.json` already has `"declaration": true`, so they are auto-generated during build. Committing them to `src/` means they can silently drift from the actual `.ts` sources. Remove these three files (`index.d.ts`, `transloco-utils.d.ts`, `transloco-utils.types.d.ts`) and add a `.gitignore` rule to prevent re-introduction.</comment>
<file context>
@@ -0,0 +1,24 @@
+export interface TranslocoGlobalConfig {
+ rootTranslationsPath?: string;
+ defaultLang?: string;
</file context>
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Issue Number: N/A
What is the new behavior?
Does this PR introduce a breaking change?
Other information
Summary by cubic
Upgrade workspace to Angular 20.3 and Nx 21 with TypeScript 5.9, and modernize initialization and tooling to keep all Transloco packages working with Angular 20.
Migration
APP_INITIALIZERwithprovideAppInitializer+inject()intransloco-persist-lang,transloco-preload-langs, andTranslocoTestingModule.moduleResolution:bundlerfor Angular libs;nodefor Node-targeted libs (transloco-utils,transloco-optimize,transloco-validator,transloco-scoped-libs).getJestProjectsAsync(); add explicit testtsconfigwhere needed.TranslocoPipeimports for standalone-by-default; default locale toen-USin locale mocks.standalone: truefrom demo components; enablecontinuous: trueforserve.@angular-eslint/prefer-injectacross packages (constructor-injection refactor deferred)..d.tstypings fortransloco-utilsexports (getGlobalConfig,TranslocoGlobalConfig).Dependencies
20.3.9, Nx21.6.9, TypeScript5.9.3.ng-packagr20.3.2,jest-preset-angular14.6.2,cypress14.5.4,zone.js0.15.1,chokidar4.x,@angular-eslint/*20.7.0.Written for commit 0f1eb90. Summary will update on new commits.
Summary by CodeRabbit
Release Notes
Chores
Refactor