Skip to content

feat(nx): update to Nx 22#194

Merged
tbinna merged 2 commits intomainfrom
beta
Mar 27, 2026
Merged

feat(nx): update to Nx 22#194
tbinna merged 2 commits intomainfrom
beta

Conversation

@tbinna
Copy link
Copy Markdown
Member

@tbinna tbinna commented Mar 27, 2026

feat(nx): update to Nx 22

BREAKING CHANGE: requires Nx 22, removed build and install executors

Closes #181

Summary by CodeRabbit

  • New Features

    • Added ESLint flat configuration support
    • Added JSONata syntax highlighting for documentation
  • Deprecated/Removed

    • Removed deprecated build executor
    • Removed deprecated install executor
  • Documentation

    • Updated command syntax throughout guides to use Nx target-based invocation
    • Updated terminology from "executors" to "targets"
  • Dependencies

    • Upgraded Nx toolchain from 20.3.1 to 22.6.1
    • Upgraded ESLint to v9, TypeScript to 5.9.3, Jest to 30.0.5
  • Tests

    • Updated E2E tests to use new Nx command syntax
    • Removed build executor test suite

tbinna added 2 commits March 26, 2026 18:47
BREAKING CHANGE: requires Nx 22, removed build and install executors

Closes #181
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

📝 Walkthrough

Walkthrough

The pull request upgrades the nx-forge plugin to be compatible with Nx 22, removing deprecated build and install executors, migrating ESLint configuration to the flat config format, updating command syntax from shorthand to explicit target invocation (nx run <app>:<target>), and upgrading dependencies across the ecosystem.

Changes

Cohort / File(s) Summary
ESLint Configuration Migration
.eslintrc.json, eslint.config.mjs, packages/nx-forge/.eslintrc.json, packages/nx-forge/eslint.config.mjs, tools/docs/.eslintrc.json, tools/docs/eslint.config.mjs
Removed legacy .eslintrc.json configurations and added new flat config files (eslint.config.mjs) across workspace root and tool packages, composing Nx plugins and dependency checks with dynamic jsonc-eslint-parser imports.
Build Executor Removal
packages/nx-forge/executors.json, packages/nx-forge/src/executors/build/executor.ts, packages/nx-forge/src/executors/build/lib/compile-webpack.ts, packages/nx-forge/src/executors/build/lib/generate-package-json.ts, packages/nx-forge/src/executors/build/lib/normalize-options.ts, packages/nx-forge/src/executors/build/schema.d.ts, packages/nx-forge/src/executors/build/schema.json
Deleted the entire deprecated build executor implementation and its supporting helper libraries, removing webpack compilation, package.json generation, and manifest patching logic previously handled by the build step.
Install Executor Removal
packages/nx-forge/executors.json, packages/nx-forge/src/executors/install/executor.ts, packages/nx-forge/src/executors/install/schema.d.ts, packages/nx-forge/src/executors/install/schema.json, packages/nx-forge/src/shared/targets/get-install-config.ts
Removed the deprecated install executor that invoked forge install commands; functionality should now be performed directly via the Forge CLI.
Package Executor Migration
packages/nx-forge/src/executors/package/executor.ts, packages/nx-forge/src/executors/package/lib/copy-forge-app-assets.ts, packages/nx-forge/src/executors/package/lib/create-package-json.ts, packages/nx-forge/src/executors/package/lib/patch-manifest-yml.ts, packages/nx-forge/src/executors/package/lib/process-resource-dependencies.ts
Updated package executor to import shared helpers from local ./lib/... paths instead of ../build/lib/... (consolidating build-related helpers into package executor), simplified control flow in manifest patching and resource processing.
Documentation Command Syntax Updates
docs/guides/generating-a-forge-app.md, docs/guides/getting-started.md, docs/reference/executors.md, packages/nx-forge/src/generators/application/files/README.md
Updated command examples from shorthand (nx build <app>, nx package <app>) to explicit target syntax (nx run <app>:build, nx run <app>:package); updated install references to use direct Forge CLI instead of Nx executor.
Documentation Terminology & Reference Updates
docs/concepts/plugin-concepts.md, docs/guides/adding-a-ui-kit-module.md, docs/guides/migrating-to-inferred-tasks.md, docs/guides/migrating-to-package-executor.md, docs/guides/transforming-the-manifest.md, AGENTS.md
Changed terminology from "executors" to "targets", updated migration guides to reflect build executor deprecation, added technology stack documentation, and adjusted JSONata example formatting.
E2E Test Infrastructure
e2e/nx-forge-e2e/tests/utils/async-commands.ts, e2e/nx-forge-e2e/tests/utils/generate-forge-app.ts, e2e/nx-forge-e2e/tests/application.generator.spec.ts
Added runCommandAsync and new runNxCommandAsync utilities with environment variable sanitization, moved runNxCommandAsync to local utilities instead of importing from @nx/plugin/testing; refactored to support flexible command execution.
E2E Test Suite Updates
e2e/nx-forge-e2e/tests/basic-setup.spec.ts, e2e/nx-forge-e2e/tests/build.executor.spec.ts
Updated basic-setup tests to use explicit target syntax and Forge CLI for install operations; removed entire build executor E2E test suite (deprecated feature).
Dependency Upgrades
package.json, packages/nx-forge/package.json
Upgraded Nx toolchain from 20.3.1 to 22.6.1, upgraded ESLint to ^9.8.0, TypeScript to 5.9.3, Jest to 30.0.5, and related TypeScript-ESLint tooling to ^8.40.0; added @eslint/eslintrc for flat config compatibility; removed unused runtime dependencies from root.
Application Generator Updates
packages/nx-forge/src/generators/application/files/tsconfig.app.json, packages/nx-forge/src/generators/application/generator.spec.ts, packages/nx-forge/src/generators/application/files/manifest.yml
Removed JSX compiler options from tsconfig, updated test assertions for Jest config (jest.config.cts) and ESLint config (eslint.config.mjs), bumped Node.js runtime from nodejs22.x to nodejs24.x.
Configuration & Workspace Updates
.codex/config.toml, .eslintignore, .gitignore, nx.json, docs/.vitepress/config.mts, docs/.vitepress/grammars/jsonata.tmLanguage.json, packages/nx-forge/project.json, packages/nx-forge/jest.config.ts
Added Codex sandbox configuration, updated gitignore patterns, added ESLint flat config to Nx cache inputs, disabled Nx analytics, added JSONata TextMate grammar for VitePress documentation, reorganized project.json tags, removed ESLint disable comment from Jest config.
Graph & Target Generation
packages/nx-forge/src/graph/create-nodes.ts
Stopped generating install target in inferred task configuration; removed getInstallConfig import.
Migrations
packages/nx-forge/src/migrations/update-2-3-0/add-nx-forge-plugin.ts, packages/nx-forge/src/migrations/update-2-3-0/remove-implicit-custom-ui-dependencies.ts, packages/nx-forge/src/migrations/update-3-0-0-webpack-config-setup/update-3-0-0-webpack-config-setup.ts, tools/scripts/start-local-registry.ts
Removed ESLint disable comments, replaced BuildExecutorOptions with inline LegacyBuildExecutorOptions interface in webpack config migration, updated version action configuration parameter names.
Reference Documentation Tests
tools/docs/src/lib/reference-docs.spec.ts
Updated test expectations to reflect package and register executors instead of deprecated build and install; adjusted Markdown content assertions for option schemas.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 From executors old to targets bright and new,
Nx twenty-two arrives with modern views!
We hop and bound through ESLint's flatter lands,
Node24 awaits with stronger, steady hands.
Build and install retire their noble quest,
As package and forge lead onward with zest! 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(nx): update to Nx 22' accurately reflects the main change - upgrading the project to be compatible with Nx 22 as described in the PR objectives.
Linked Issues check ✅ Passed The PR successfully implements Nx 22 compatibility requirements from issue #181: updated Nx toolchain to version 22.6.1, migrated ESLint configuration to flat config format, removed deprecated build and install executors, and updated all documentation and examples accordingly.
Out of Scope Changes check ✅ Passed All code changes are scoped to Nx 22 compatibility: ESLint migration, executor deprecation, documentation updates, dependency upgrades, and configuration adjustments. No unrelated changes detected outside the PR objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch beta

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 (1)
e2e/nx-forge-e2e/tests/utils/async-commands.ts (1)

100-106: ⚠️ Potential issue | 🟡 Minor

Inconsistent package manager detection when cwd is provided.

runNxCommandAsync detects the package manager based on opts.cwd, but runForgeCommandAsync calls getPackageManagerCommand() without arguments, using the default detection path. If a caller provides a custom cwd (as seen in Context snippet 4), the Forge command may run with a different package manager than expected.

🔧 Proposed fix to detect package manager based on cwd
 export const runForgeCommandAsync = (
   command: string,
   opts: { silenceError?: boolean; env?: NodeJS.ProcessEnv; cwd?: string } = {
     silenceError: false,
   }
 ): Promise<{ stdout: string; stderr: string }> => {
-  const pmc = getPackageManagerCommand();
+  const cwd = opts.cwd ?? tmpProjPath();
+  const pmc = getPackageManagerCommand(detectPackageManager(cwd));
   return new Promise((resolve, reject) => {
     exec(
       `${pmc.exec} forge ${command}`,
       {
-        cwd: opts.cwd ?? tmpProjPath(),
+        cwd,
         env: getCommandEnv(opts.env),
       },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/nx-forge-e2e/tests/utils/async-commands.ts` around lines 100 - 106, The
package manager detection in runForgeCommandAsync is inconsistent because pmc is
obtained via getPackageManagerCommand() without considering the caller-provided
opts.cwd; update runForgeCommandAsync to compute the package manager using the
same cwd logic as runNxCommandAsync (i.e., call
getPackageManagerCommand(opts.cwd ?? tmpProjPath())), so pmc reflects the
correct project directory before building the exec string that uses
getCommandEnv(opts.env) and opts.cwd.
🧹 Nitpick comments (12)
docs/.vitepress/grammars/jsonata.tmLanguage.json (1)

75-84: Inconsistent scope name for single-line comments.

The scope name uses .js suffix instead of .jsonata for consistency with the rest of the grammar.

🔧 Proposed fix
         {
           "captures": {
             "1": {
               "name": "punctuation.definition.comment.jsonata"
             }
           },
           "match": "(//).*$\\n?",
-          "name": "comment.line.double-slash.js"
+          "name": "comment.line.double-slash.jsonata"
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/grammars/jsonata.tmLanguage.json` around lines 75 - 84, The
grammar's single-line comment token currently uses the scope
"comment.line.double-slash.js" which is inconsistent with the rest of the
JSONata grammar; change the scope name to use the .jsonata suffix (e.g.,
"comment.line.double-slash.jsonata") so it matches the existing
"punctuation.definition.comment.jsonata" capture and the rest of the grammar;
update the "name" value in the rule that contains the "match": "(//).*$\\n?" and
the "captures" entry to use the .jsonata scope.
docs/.vitepress/config.mts (1)

1-3: Unused import: resolve from node:path.

The resolve function is imported but never used in this file. Consider removing it to keep imports clean.

🔧 Proposed fix
 import { readFileSync } from 'node:fs';
 import { fileURLToPath } from 'node:url';
-import { resolve } from 'node:path';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/config.mts` around lines 1 - 3, The import list includes an
unused symbol `resolve` from 'node:path'—remove `resolve` from the import
declaration (the line importing readFileSync, fileURLToPath, resolve) so only
the used symbols (readFileSync and fileURLToPath) are imported to clean up
unused imports.
tools/docs/eslint.config.mjs (2)

33-44: Consider extracting the shared parser import.

The jsonc-eslint-parser is dynamically imported twice (lines 34 and 43). You could extract it to a shared constant to avoid duplicate imports.

🔧 Proposed optimization
 import baseConfig from '../../eslint.config.mjs';
+
+const jsoncParser = await import('jsonc-eslint-parser');

 export default [
   ...baseConfig,
   // ... other configs ...
   {
     files: ['**/*.json'],
     rules: {
       '@nx/dependency-checks': [
         'error',
         {
           ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs}'],
         },
       ],
     },
     languageOptions: {
-      parser: await import('jsonc-eslint-parser'),
+      parser: jsoncParser,
     },
   },
   {
     files: ['./package.json'],
     rules: {
       '@nx/nx-plugin-checks': 'error',
     },
     languageOptions: {
-      parser: await import('jsonc-eslint-parser'),
+      parser: jsoncParser,
     },
   },
 ];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/docs/eslint.config.mjs` around lines 33 - 44, Extract the repeated
dynamic import of 'jsonc-eslint-parser' into a shared constant (e.g.,
jsoncParser) and reuse it in both languageOptions.parser entries to avoid
duplicate imports; update the two locations that currently set
languageOptions.parser: await import('jsonc-eslint-parser') so they reference
the shared constant instead.

4-7: Duplicate ignores pattern.

The ignores for **/dist and **/out-tsc on lines 4-6 duplicate what's already defined in baseConfig (from root eslint.config.mjs line 14). Since baseConfig is spread after, its ignores will also apply. Consider removing the local ignores block unless there's a specific ordering requirement.

🔧 Proposed simplification
 export default [
-  {
-    ignores: ['**/dist', '**/out-tsc'],
-  },
   ...baseConfig,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/docs/eslint.config.mjs` around lines 4 - 7, The local ignores block in
tools/docs/eslint.config.mjs duplicates patterns already present in baseConfig;
remove the local ignores: ['**/dist', '**/out-tsc'] (or the entire ignores
object) so you only spread ...baseConfig, or if you need to keep it for
ordering, merge uniqued patterns into baseConfig instead—update the
configuration around the ignores property and the spread of baseConfig to
eliminate the duplicate patterns while keeping the effective ignore list
identical.
eslint.config.mjs (1)

1-10: FlatCompat is imported but not used in the configuration.

The compat instance created from FlatCompat is never used in the exported configuration array. If this is intentional scaffolding for future compatibility shims, consider adding a comment; otherwise, remove the unused import and instantiation.

🔧 Proposed fix to remove unused code
-import { FlatCompat } from '@eslint/eslintrc';
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
 import js from '@eslint/js';
 import nx from '@nx/eslint-plugin';
-
-const compat = new FlatCompat({
-  baseDirectory: dirname(fileURLToPath(import.meta.url)),
-  recommendedConfig: js.configs.recommended,
-});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eslint.config.mjs` around lines 1 - 10, The file creates a FlatCompat
instance named compat but never uses it; either remove the unused import and the
const compat = new FlatCompat(...) line (and the FlatCompat import) or wire
compat into the exported ESLint configuration depending on intent; locate the
FlatCompat import and the compat variable in eslint.config.mjs and either delete
both or replace the unused instantiation with code that applies compat to the
exported config so the symbol FlatCompat/compat is actually used.
docs/reference/executors.md (1)

85-89: Consider updating tunnel command syntax for consistency.

The tunnel executor documentation on line 88 still uses the shorthand syntax nx tunnel <nx-forge-app-name>, while all other executors in this document were updated to use nx run <project>:<target>. For consistency, consider updating to:

nx run <nx-forge-app-name>:tunnel
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/reference/executors.md` around lines 85 - 89, Update the Tunnel executor
example to use the consistent "nx run" syntax: replace the shorthand example
command `nx tunnel <nx-forge-app-name>` with `nx run <nx-forge-app-name>:tunnel`
in the docs; ensure the code block under the "Tunnel" heading and any references
to the `tunnel` executor reflect this `nx run <project>:<target>` pattern.
nx.json (2)

26-31: Consider removing legacy .eslintrc.json reference after migration.

The lint inputs include both {workspaceRoot}/.eslintrc.json and {workspaceRoot}/eslint.config.mjs. Since the PR removes the legacy .eslintrc.json files in favor of flat configs, the old reference on line 29 can be removed to clean up the configuration.

🔧 Proposed cleanup
     "@nx/eslint:lint": {
-      "inputs": [
-        "default",
-        "{workspaceRoot}/.eslintrc.json",
-        "{workspaceRoot}/eslint.config.mjs"
-      ],
+      "inputs": ["default", "{workspaceRoot}/eslint.config.mjs"],
       "cache": true
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nx.json` around lines 26 - 31, Remove the legacy `.eslintrc.json` reference
from the "@nx/eslint:lint" inputs array: delete the
"{workspaceRoot}/.eslintrc.json" entry so the inputs only reference the flat
config "{workspaceRoot}/eslint.config.mjs" (update the inputs array in the
"@nx/eslint:lint" block accordingly).

48-51: Same cleanup opportunity for production inputs.

Similar to the lint inputs, the production exclusion still references {projectRoot}/.eslintrc.json on line 48, which can be removed if all legacy ESLint configs have been deleted.

🔧 Proposed cleanup
     "production": [
       "default",
       "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
       "!{projectRoot}/tsconfig.spec.json",
       "!{projectRoot}/jest.config.[jt]s",
-      "!{projectRoot}/.eslintrc.json",
       "!{projectRoot}/src/test-setup.[jt]s",
       "!{projectRoot}/eslint.config.mjs"
     ]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nx.json` around lines 48 - 51, Remove the stale exclusion entry
"!{projectRoot}/.eslintrc.json" from the production inputs array in nx.json (the
same cleanup applied to lint inputs); locate the production inputs list and
delete the string "!{projectRoot}/.eslintrc.json" so the config no longer
references the legacy ESLint file.
packages/nx-forge/eslint.config.mjs (2)

19-28: Consider removing empty placeholder rule blocks.

These override blocks with empty rules: {} don't modify any behavior. If they're intentional extension points for future use, consider adding a more descriptive comment; otherwise, they can be removed to reduce noise.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nx-forge/eslint.config.mjs` around lines 19 - 28, The two override
blocks that target files ['**/*.ts','**/*.tsx'] and ['**/*.js','**/*.jsx'] only
contain empty rules objects and thus have no effect; either remove these
override entries entirely from the ESLint config (the objects containing files:
[...] and rules: {}) or replace the empty rules with a short explanatory comment
indicating they are intentional placeholders for future rule customizations so
reviewers understand they’re deliberate.

39-55: Consider extracting the duplicated parser import.

The await import('jsonc-eslint-parser') is called twice (lines 40 and 54). Extract it to a single variable to avoid redundancy.

♻️ Proposed refactor
 import baseConfig from '../../eslint.config.mjs';

+const jsoncParser = await import('jsonc-eslint-parser');
+
 export default [
   {
     ignores: ['**/dist', '**/out-tsc'],
   },
   ...baseConfig,
   // ... other configs ...
   {
     files: [
       './package.json',
       './generators.json',
       './executors.json',
       './migrations.json',
     ],
     rules: {
       '@nx/nx-plugin-checks': 'error',
     },
     languageOptions: {
-      parser: await import('jsonc-eslint-parser'),
+      parser: jsoncParser,
     },
   },
   {
     files: ['./package.json'],
     rules: {
       '@nx/dependency-checks': [
         'error',
         {
           ignoredDependencies: ['nx'],
         },
       ],
     },
     languageOptions: {
-      parser: await import('jsonc-eslint-parser'),
+      parser: jsoncParser,
     },
   },
 ];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nx-forge/eslint.config.mjs` around lines 39 - 55, There are two
identical dynamic imports for jsonc-eslint-parser in the languageOptions blocks;
extract the import into a single variable (e.g., jsoncParser) and reuse that
variable in both languageOptions.parser entries instead of calling await
import('jsonc-eslint-parser') twice, updating the languageOptions.parser
references accordingly so both blocks reference the same imported parser.
packages/nx-forge/src/generators/application/generator.spec.ts (2)

342-346: Redundant assertion on line 346.

The hasJsonValue assertion on lines 331-339 already verifies that exclude contains 'jest.config.cts'. Line 346 duplicates this check.

♻️ Proposed fix to remove redundant assertion
       const nestedTsconfigApp = readJson(
         tree,
         'my-dir/my-forge-app/tsconfig.app.json'
       );
-      expect(nestedTsconfigApp.exclude).toContain('jest.config.cts');

       expect(tree.exists(eslintConfigPath('my-dir/my-forge-app'))).toBeTruthy();

Note: If you want to keep the explicit read for clarity, consider removing it from the hasJsonValue list instead to avoid testing the same thing twice.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nx-forge/src/generators/application/generator.spec.ts` around lines
342 - 346, The test contains a redundant assertion: remove the duplicate expect
that checks nestedTsconfigApp.exclude contains 'jest.config.cts'. Specifically,
drop or comment out the expect(...) line that reads the JSON into
nestedTsconfigApp and asserts .exclude includes 'jest.config.cts' (the explicit
expect using nestedTsconfigApp), since hasJsonValue already verifies this; keep
the hasJsonValue check and, if you prefer keeping the readJson for clarity,
remove 'tsconfig.app.json' related entry from the hasJsonValue assertions
instead to avoid duplicate verification.

161-169: Redundant assertion on line 169.

The toContain('jest.config.cts') check is redundant since lines 161-168 already assert the exact contents of the exclude array, which includes 'jest.config.cts'.

♻️ Proposed fix to remove redundant assertion
       expect(tsconfigApp.exclude.sort()).toEqual(
         [
           'jest.config.ts',
           'jest.config.cts',
           'src/**/*.spec.ts',
           'src/**/*.test.ts',
         ].sort()
       );
-      expect(tsconfigApp.exclude).toContain('jest.config.cts');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nx-forge/src/generators/application/generator.spec.ts` around lines
161 - 169, The test contains a redundant assertion: remove the extra expect that
checks for 'jest.config.cts' since tsconfigApp.exclude is already asserted
exactly in the preceding expect; specifically delete the line with
expect(tsconfigApp.exclude).toContain('jest.config.cts') in generator.spec.ts so
the test only verifies the full exclude array (the expect(...).toEqual(...)
assertion remains).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.codex/config.toml:
- Around line 6-8: The config entry writable_roots = ["~/.npmrc"] uses an
unexpanded tilde and will be treated literally by Codex; replace it with a
workspace-local .npmrc (e.g., writable_roots = ["./.npmrc"]) so Nx's
startLocalRegistry helper writes auth tokens to the project root rather than the
user home, or if you intentionally must target the user home, change it to an
absolute home path (e.g., /Users/USERNAME/.npmrc) instead of "~/.npmrc"; update
the writable_roots value accordingly and ensure any CI/dev docs reflect the
chosen pattern.

In `@package.json`:
- Around line 47-50: The package.json has a version mismatch: update the
dependency "jest-environment-node" to match Jest's version (set it to "30.0.5"
to align with "jest" and "jest-environment-jsdom"); after changing
"jest-environment-node" update the lockfile (npm install or yarn install) and
run the test suite to verify no environment-related failures. Refer to the
manifest entries "jest", "jest-environment-jsdom", and "jest-environment-node"
when making the change.

---

Outside diff comments:
In `@e2e/nx-forge-e2e/tests/utils/async-commands.ts`:
- Around line 100-106: The package manager detection in runForgeCommandAsync is
inconsistent because pmc is obtained via getPackageManagerCommand() without
considering the caller-provided opts.cwd; update runForgeCommandAsync to compute
the package manager using the same cwd logic as runNxCommandAsync (i.e., call
getPackageManagerCommand(opts.cwd ?? tmpProjPath())), so pmc reflects the
correct project directory before building the exec string that uses
getCommandEnv(opts.env) and opts.cwd.

---

Nitpick comments:
In `@docs/.vitepress/config.mts`:
- Around line 1-3: The import list includes an unused symbol `resolve` from
'node:path'—remove `resolve` from the import declaration (the line importing
readFileSync, fileURLToPath, resolve) so only the used symbols (readFileSync and
fileURLToPath) are imported to clean up unused imports.

In `@docs/.vitepress/grammars/jsonata.tmLanguage.json`:
- Around line 75-84: The grammar's single-line comment token currently uses the
scope "comment.line.double-slash.js" which is inconsistent with the rest of the
JSONata grammar; change the scope name to use the .jsonata suffix (e.g.,
"comment.line.double-slash.jsonata") so it matches the existing
"punctuation.definition.comment.jsonata" capture and the rest of the grammar;
update the "name" value in the rule that contains the "match": "(//).*$\\n?" and
the "captures" entry to use the .jsonata scope.

In `@docs/reference/executors.md`:
- Around line 85-89: Update the Tunnel executor example to use the consistent
"nx run" syntax: replace the shorthand example command `nx tunnel
<nx-forge-app-name>` with `nx run <nx-forge-app-name>:tunnel` in the docs;
ensure the code block under the "Tunnel" heading and any references to the
`tunnel` executor reflect this `nx run <project>:<target>` pattern.

In `@eslint.config.mjs`:
- Around line 1-10: The file creates a FlatCompat instance named compat but
never uses it; either remove the unused import and the const compat = new
FlatCompat(...) line (and the FlatCompat import) or wire compat into the
exported ESLint configuration depending on intent; locate the FlatCompat import
and the compat variable in eslint.config.mjs and either delete both or replace
the unused instantiation with code that applies compat to the exported config so
the symbol FlatCompat/compat is actually used.

In `@nx.json`:
- Around line 26-31: Remove the legacy `.eslintrc.json` reference from the
"@nx/eslint:lint" inputs array: delete the "{workspaceRoot}/.eslintrc.json"
entry so the inputs only reference the flat config
"{workspaceRoot}/eslint.config.mjs" (update the inputs array in the
"@nx/eslint:lint" block accordingly).
- Around line 48-51: Remove the stale exclusion entry
"!{projectRoot}/.eslintrc.json" from the production inputs array in nx.json (the
same cleanup applied to lint inputs); locate the production inputs list and
delete the string "!{projectRoot}/.eslintrc.json" so the config no longer
references the legacy ESLint file.

In `@packages/nx-forge/eslint.config.mjs`:
- Around line 19-28: The two override blocks that target files
['**/*.ts','**/*.tsx'] and ['**/*.js','**/*.jsx'] only contain empty rules
objects and thus have no effect; either remove these override entries entirely
from the ESLint config (the objects containing files: [...] and rules: {}) or
replace the empty rules with a short explanatory comment indicating they are
intentional placeholders for future rule customizations so reviewers understand
they’re deliberate.
- Around line 39-55: There are two identical dynamic imports for
jsonc-eslint-parser in the languageOptions blocks; extract the import into a
single variable (e.g., jsoncParser) and reuse that variable in both
languageOptions.parser entries instead of calling await
import('jsonc-eslint-parser') twice, updating the languageOptions.parser
references accordingly so both blocks reference the same imported parser.

In `@packages/nx-forge/src/generators/application/generator.spec.ts`:
- Around line 342-346: The test contains a redundant assertion: remove the
duplicate expect that checks nestedTsconfigApp.exclude contains
'jest.config.cts'. Specifically, drop or comment out the expect(...) line that
reads the JSON into nestedTsconfigApp and asserts .exclude includes
'jest.config.cts' (the explicit expect using nestedTsconfigApp), since
hasJsonValue already verifies this; keep the hasJsonValue check and, if you
prefer keeping the readJson for clarity, remove 'tsconfig.app.json' related
entry from the hasJsonValue assertions instead to avoid duplicate verification.
- Around line 161-169: The test contains a redundant assertion: remove the extra
expect that checks for 'jest.config.cts' since tsconfigApp.exclude is already
asserted exactly in the preceding expect; specifically delete the line with
expect(tsconfigApp.exclude).toContain('jest.config.cts') in generator.spec.ts so
the test only verifies the full exclude array (the expect(...).toEqual(...)
assertion remains).

In `@tools/docs/eslint.config.mjs`:
- Around line 33-44: Extract the repeated dynamic import of
'jsonc-eslint-parser' into a shared constant (e.g., jsoncParser) and reuse it in
both languageOptions.parser entries to avoid duplicate imports; update the two
locations that currently set languageOptions.parser: await
import('jsonc-eslint-parser') so they reference the shared constant instead.
- Around line 4-7: The local ignores block in tools/docs/eslint.config.mjs
duplicates patterns already present in baseConfig; remove the local ignores:
['**/dist', '**/out-tsc'] (or the entire ignores object) so you only spread
...baseConfig, or if you need to keep it for ordering, merge uniqued patterns
into baseConfig instead—update the configuration around the ignores property and
the spread of baseConfig to eliminate the duplicate patterns while keeping the
effective ignore list identical.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: cef6d0fe-983a-4bd4-b5bb-590cdf1de712

📥 Commits

Reviewing files that changed from the base of the PR and between 2247e2f and b603f55.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (56)
  • .codex/config.toml
  • .eslintignore
  • .eslintrc.json
  • .gitignore
  • AGENTS.md
  • docs/.vitepress/config.mts
  • docs/.vitepress/grammars/jsonata.tmLanguage.json
  • docs/concepts/plugin-concepts.md
  • docs/guides/adding-a-ui-kit-module.md
  • docs/guides/generating-a-forge-app.md
  • docs/guides/getting-started.md
  • docs/guides/migrating-to-inferred-tasks.md
  • docs/guides/migrating-to-package-executor.md
  • docs/guides/transforming-the-manifest.md
  • docs/reference/executors.md
  • e2e/nx-forge-e2e/tests/application.generator.spec.ts
  • e2e/nx-forge-e2e/tests/basic-setup.spec.ts
  • e2e/nx-forge-e2e/tests/build.executor.spec.ts
  • e2e/nx-forge-e2e/tests/utils/async-commands.ts
  • e2e/nx-forge-e2e/tests/utils/generate-forge-app.ts
  • eslint.config.mjs
  • nx.json
  • package.json
  • packages/nx-forge/.eslintrc.json
  • packages/nx-forge/eslint.config.mjs
  • packages/nx-forge/executors.json
  • packages/nx-forge/jest.config.ts
  • packages/nx-forge/package.json
  • packages/nx-forge/project.json
  • packages/nx-forge/src/executors/build/executor.ts
  • packages/nx-forge/src/executors/build/lib/compile-webpack.ts
  • packages/nx-forge/src/executors/build/lib/generate-package-json.ts
  • packages/nx-forge/src/executors/build/lib/normalize-options.ts
  • packages/nx-forge/src/executors/build/schema.d.ts
  • packages/nx-forge/src/executors/build/schema.json
  • packages/nx-forge/src/executors/install/executor.ts
  • packages/nx-forge/src/executors/install/schema.d.ts
  • packages/nx-forge/src/executors/install/schema.json
  • packages/nx-forge/src/executors/package/executor.ts
  • packages/nx-forge/src/executors/package/lib/copy-forge-app-assets.ts
  • packages/nx-forge/src/executors/package/lib/create-package-json.ts
  • packages/nx-forge/src/executors/package/lib/patch-manifest-yml.ts
  • packages/nx-forge/src/executors/package/lib/process-resource-dependencies.ts
  • packages/nx-forge/src/generators/application/files/README.md
  • packages/nx-forge/src/generators/application/files/manifest.yml
  • packages/nx-forge/src/generators/application/files/tsconfig.app.json
  • packages/nx-forge/src/generators/application/generator.spec.ts
  • packages/nx-forge/src/graph/create-nodes.ts
  • packages/nx-forge/src/migrations/update-2-3-0/add-nx-forge-plugin.ts
  • packages/nx-forge/src/migrations/update-2-3-0/remove-implicit-custom-ui-dependencies.ts
  • packages/nx-forge/src/migrations/update-3-0-0-webpack-config-setup/update-3-0-0-webpack-config-setup.ts
  • packages/nx-forge/src/shared/targets/get-install-config.ts
  • tools/docs/.eslintrc.json
  • tools/docs/eslint.config.mjs
  • tools/docs/src/lib/reference-docs.spec.ts
  • tools/scripts/start-local-registry.ts
💤 Files with no reviewable changes (22)
  • .eslintignore
  • packages/nx-forge/src/executors/package/lib/copy-forge-app-assets.ts
  • packages/nx-forge/jest.config.ts
  • packages/nx-forge/src/graph/create-nodes.ts
  • packages/nx-forge/src/migrations/update-2-3-0/add-nx-forge-plugin.ts
  • packages/nx-forge/src/executors/package/lib/create-package-json.ts
  • e2e/nx-forge-e2e/tests/build.executor.spec.ts
  • packages/nx-forge/src/shared/targets/get-install-config.ts
  • .eslintrc.json
  • packages/nx-forge/src/migrations/update-2-3-0/remove-implicit-custom-ui-dependencies.ts
  • packages/nx-forge/executors.json
  • tools/docs/.eslintrc.json
  • packages/nx-forge/src/executors/install/executor.ts
  • packages/nx-forge/src/executors/build/schema.json
  • packages/nx-forge/src/executors/build/lib/compile-webpack.ts
  • packages/nx-forge/src/executors/build/lib/normalize-options.ts
  • packages/nx-forge/src/executors/install/schema.json
  • packages/nx-forge/src/executors/build/executor.ts
  • packages/nx-forge/.eslintrc.json
  • packages/nx-forge/src/executors/build/schema.d.ts
  • packages/nx-forge/src/executors/install/schema.d.ts
  • packages/nx-forge/src/executors/build/lib/generate-package-json.ts

Comment on lines +6 to +8
# Nx's startLocalRegistry helper shells out to `npm config set/delete ... --ws=false`,
# which temporarily writes the local registry auth token to the user npm config at ~/.npmrc.
writable_roots = ["~/.npmrc"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

For Codex .codex/config.toml, does writable_roots = ["~/.npmrc"]support tilde expansion, or must paths be absolute? Also, what is the recommended secure pattern to allow temporary npm auth for Nx local registry helpers without writing to user-home.npmrc?

💡 Result:

For Codex .codex/config.toml, writable_roots = ["~/.npmrc"] does not support tilde expansion; paths must be absolute. Official docs and examples consistently use absolute paths like ["/Users/YOU/.pyenv/shims"] or ["/additional/writable/path"]. A related GitHub issue confirms that Codex tools like Edit do not expand ~, treating it literally, which aligns with config path handling requiring explicit expansion or absolute paths. The recommended secure pattern for temporary npm auth with Nx local registry helpers, without writing to user-home .npmrc, is to use project-level .npmrc in the workspace root. Nx docs state this configures custom registries and auth tokens for publishing/installing, scoped by npm scopes (e.g., @my-scope:registry=... //my-custom-registry.com/:_authToken=). This avoids global user config, keeping auth local to the project. Environment variables like NPM_TOKEN can also inject auth temporarily via CI or shell_environment_policy in Codex config, but project .npmrc is the explicit Nx recommendation.

Citations:


Replace user-home .npmrc reference with workspace-local .npmrc and use absolute path if keeping this approach.

The writable_roots = ["~/.npmrc"] path will not work as intended: Codex does not expand tilde in writable_roots, so it will treat ~/.npmrc literally rather than resolving to the user's home directory. This is both a config bug and a security concern.

The recommended approach is to use a workspace-local .npmrc in the project root instead. This allows Nx's local registry helper to configure auth tokens scoped to the project without writing to user-home config, eliminating cross-run/cross-user persistence risks. If you must write to user-home ~/.npmrc, use an absolute path like /Users/USERNAME/.npmrc—but the workspace-local .npmrc pattern is safer and aligns with Nx documentation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.codex/config.toml around lines 6 - 8, The config entry writable_roots =
["~/.npmrc"] uses an unexpanded tilde and will be treated literally by Codex;
replace it with a workspace-local .npmrc (e.g., writable_roots = ["./.npmrc"])
so Nx's startLocalRegistry helper writes auth tokens to the project root rather
than the user home, or if you intentionally must target the user home, change it
to an absolute home path (e.g., /Users/USERNAME/.npmrc) instead of "~/.npmrc";
update the writable_roots value accordingly and ensure any CI/dev docs reflect
the chosen pattern.

Comment on lines +47 to +50
"jest": "30.0.5",
"jest-environment-jsdom": "30.0.5",
"jest-environment-node": "^29.4.1",
"nx": "20.3.1",
"jest-util": "30.0.5",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Version mismatch: jest-environment-node is at ^29.4.1 while Jest is at 30.0.5.

The jest-environment-jsdom is correctly updated to 30.0.5, but jest-environment-node remains at ^29.4.1. This version mismatch may cause test failures or unexpected behavior when tests use the Node.js environment.

Proposed fix
     "jest": "30.0.5",
     "jest-environment-jsdom": "30.0.5",
-    "jest-environment-node": "^29.4.1",
+    "jest-environment-node": "30.0.5",
     "jest-util": "30.0.5",
📝 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.

Suggested change
"jest": "30.0.5",
"jest-environment-jsdom": "30.0.5",
"jest-environment-node": "^29.4.1",
"nx": "20.3.1",
"jest-util": "30.0.5",
"jest": "30.0.5",
"jest-environment-jsdom": "30.0.5",
"jest-environment-node": "30.0.5",
"jest-util": "30.0.5",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` around lines 47 - 50, The package.json has a version mismatch:
update the dependency "jest-environment-node" to match Jest's version (set it to
"30.0.5" to align with "jest" and "jest-environment-jsdom"); after changing
"jest-environment-node" update the lockfile (npm install or yarn install) and
run the test suite to verify no environment-related failures. Refer to the
manifest entries "jest", "jest-environment-jsdom", and "jest-environment-node"
when making the change.

@tbinna tbinna merged commit 769252e into main Mar 27, 2026
8 checks passed
@tbinna tbinna deleted the beta branch March 27, 2026 10:25
@github-actions
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 7.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nx 22

1 participant