Skip to content
This repository was archived by the owner on May 3, 2026. It is now read-only.

NX Plugin + Bundle Webpack/Rollup plugins#209

Open
matteobruni wants to merge 19 commits intomainfrom
dev
Open

NX Plugin + Bundle Webpack/Rollup plugins#209
matteobruni wants to merge 19 commits intomainfrom
dev

Conversation

@matteobruni
Copy link
Copy Markdown
Contributor

@matteobruni matteobruni commented May 1, 2026

Summary by CodeRabbit

  • New Features

    • Added Nx workspace plugin for enhanced monorepo management
    • Introduced ESLint, Prettier, and TypeScript compilation as standalone CLI commands
    • Added Rollup bundling support alongside existing Webpack bundling
  • Chores

    • Updated ESLint to ^10.3.0 across packages
    • Enhanced CLI documentation with installation and usage examples

Copilot AI review requested due to automatic review settings May 1, 2026 23:36
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

Important

Review skipped

Too many files!

This PR contains 197 files, which is 47 over the limit of 150.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1102a8f6-7c84-4660-9234-64cc65f62101

📥 Commits

Reviewing files that changed from the base of the PR and between ad3e416 and 7883212.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • utils/.opencode/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (197)
  • README.md
  • commands/build-bundle-rollup/.dependency-cruiser.cjs
  • commands/build-bundle-rollup/package.json
  • commands/build-bundle-webpack/package.json
  • commands/build-circular-deps/package.json
  • commands/build-clear/package.json
  • commands/build-distfiles/package.json
  • commands/build-diststats/package.json
  • commands/build-eslint/package.json
  • commands/build-prettier/package.json
  • commands/build-tsc/package.json
  • commands/build/package.json
  • commands/create-utils/package.json
  • commands/create/package.json
  • package.json
  • packages/cli/package.json
  • packages/nx-plugin/README.md
  • packages/nx-plugin/package.json
  • packages/nx-plugin/src/canonical-targets.ts
  • pnpm-workspace.yaml
  • utils/.agents/skills/link-workspace-packages/SKILL.md
  • utils/.agents/skills/monitor-ci/SKILL.md
  • utils/.agents/skills/monitor-ci/references/fix-flows.md
  • utils/.agents/skills/monitor-ci/scripts/ci-poll-decide.mjs
  • utils/.agents/skills/monitor-ci/scripts/ci-state-update.mjs
  • utils/.agents/skills/nx-generate/SKILL.md
  • utils/.agents/skills/nx-import/SKILL.md
  • utils/.agents/skills/nx-import/references/ESLINT.md
  • utils/.agents/skills/nx-import/references/GRADLE.md
  • utils/.agents/skills/nx-import/references/JEST.md
  • utils/.agents/skills/nx-import/references/NEXT.md
  • utils/.agents/skills/nx-import/references/TURBOREPO.md
  • utils/.agents/skills/nx-import/references/VITE.md
  • utils/.agents/skills/nx-plugins/SKILL.md
  • utils/.agents/skills/nx-run-tasks/SKILL.md
  • utils/.agents/skills/nx-workspace/SKILL.md
  • utils/.agents/skills/nx-workspace/references/AFFECTED.md
  • utils/.claude/settings.json
  • utils/.codex/agents/ci-monitor-subagent.toml
  • utils/.codex/config.toml
  • utils/.cursor/agents/ci-monitor-subagent.md
  • utils/.cursor/commands/monitor-ci.md
  • utils/.cursor/skills/link-workspace-packages/SKILL.md
  • utils/.cursor/skills/monitor-ci/SKILL.md
  • utils/.cursor/skills/nx-generate/SKILL.md
  • utils/.cursor/skills/nx-plugins/SKILL.md
  • utils/.cursor/skills/nx-run-tasks/SKILL.md
  • utils/.cursor/skills/nx-workspace/SKILL.md
  • utils/.cursor/skills/nx-workspace/references/AFFECTED.md
  • utils/.gemini/commands/monitor-ci.toml
  • utils/.gemini/settings.json
  • utils/.gemini/skills/link-workspace-packages/skill.md
  • utils/.gemini/skills/monitor-ci/skill.md
  • utils/.gemini/skills/nx-generate/skill.md
  • utils/.gemini/skills/nx-plugins/skill.md
  • utils/.gemini/skills/nx-run-tasks/skill.md
  • utils/.gemini/skills/nx-workspace/references/AFFECTED.md
  • utils/.gemini/skills/nx-workspace/skill.md
  • utils/.github/FUNDING.yml
  • utils/.github/agents/ci-monitor-subagent.agent.md
  • utils/.github/dependabot.yml
  • utils/.github/prompts/monitor-ci.prompt.md
  • utils/.github/skills/link-workspace-packages/SKILL.md
  • utils/.github/skills/monitor-ci/SKILL.md
  • utils/.github/skills/monitor-ci/references/fix-flows.md
  • utils/.github/skills/monitor-ci/scripts/ci-poll-decide.mjs
  • utils/.github/skills/monitor-ci/scripts/ci-state-update.mjs
  • utils/.github/skills/nx-generate/SKILL.md
  • utils/.github/skills/nx-import/SKILL.md
  • utils/.github/skills/nx-import/references/ESLINT.md
  • utils/.github/skills/nx-import/references/GRADLE.md
  • utils/.github/skills/nx-import/references/JEST.md
  • utils/.github/skills/nx-import/references/NEXT.md
  • utils/.github/skills/nx-import/references/TURBOREPO.md
  • utils/.github/skills/nx-import/references/VITE.md
  • utils/.github/skills/nx-plugins/SKILL.md
  • utils/.github/skills/nx-run-tasks/SKILL.md
  • utils/.github/skills/nx-workspace/SKILL.md
  • utils/.github/skills/nx-workspace/references/AFFECTED.md
  • utils/.github/workflows/node.js-ci.yml
  • utils/.github/workflows/npm-publish.yml
  • utils/.gitignore
  • utils/.nvmrc
  • utils/.opencode/agents/ci-monitor-subagent.md
  • utils/.opencode/commands/monitor-ci.md
  • utils/.opencode/skills/link-workspace-packages/SKILL.md
  • utils/.opencode/skills/monitor-ci/SKILL.md
  • utils/.opencode/skills/monitor-ci/references/fix-flows.md
  • utils/.opencode/skills/monitor-ci/scripts/ci-poll-decide.mjs
  • utils/.opencode/skills/monitor-ci/scripts/ci-state-update.mjs
  • utils/.opencode/skills/nx-generate/SKILL.md
  • utils/.opencode/skills/nx-import/SKILL.md
  • utils/.opencode/skills/nx-import/references/ESLINT.md
  • utils/.opencode/skills/nx-import/references/GRADLE.md
  • utils/.opencode/skills/nx-import/references/JEST.md
  • utils/.opencode/skills/nx-import/references/NEXT.md
  • utils/.opencode/skills/nx-import/references/TURBOREPO.md
  • utils/.opencode/skills/nx-import/references/VITE.md
  • utils/.opencode/skills/nx-plugins/SKILL.md
  • utils/.opencode/skills/nx-run-tasks/SKILL.md
  • utils/.opencode/skills/nx-workspace/SKILL.md
  • utils/.opencode/skills/nx-workspace/references/AFFECTED.md
  • utils/.planning/codebase/ARCHITECTURE.md
  • utils/.planning/codebase/CONCERNS.md
  • utils/.planning/codebase/CONVENTIONS.md
  • utils/.planning/codebase/INTEGRATIONS.md
  • utils/.planning/codebase/STACK.md
  • utils/.planning/codebase/STRUCTURE.md
  • utils/.planning/codebase/TESTING.md
  • utils/AGENTS.md
  • utils/CHANGELOG.md
  • utils/CLAUDE.md
  • utils/LICENSE
  • utils/README.md
  • utils/browserslist-config/.gitignore
  • utils/browserslist-config/CHANGELOG.md
  • utils/browserslist-config/LICENSE
  • utils/browserslist-config/README.md
  • utils/browserslist-config/package.json
  • utils/browserslist-config/src/index.js
  • utils/depcruise-config/CHANGELOG.md
  • utils/depcruise-config/LICENSE
  • utils/depcruise-config/README.md
  • utils/depcruise-config/eslint.config.js
  • utils/depcruise-config/package.json
  • utils/depcruise-config/src/defaultConfig.ts
  • utils/depcruise-config/src/index.ts
  • utils/depcruise-config/src/loadConfig.ts
  • utils/depcruise-config/tsconfig.json
  • utils/depcruise-config/tsup.config.ts
  • utils/eslint-config/.gitignore
  • utils/eslint-config/CHANGELOG.md
  • utils/eslint-config/LICENSE
  • utils/eslint-config/README.md
  • utils/eslint-config/eslint.config.js
  • utils/eslint-config/package.json
  • utils/eslint-config/src/eslint.config.ts
  • utils/eslint-config/tsconfig.json
  • utils/opencode.json
  • utils/patches/tsup@8.5.1.patch
  • utils/prettier-config/.gitignore
  • utils/prettier-config/CHANGELOG.md
  • utils/prettier-config/LICENSE
  • utils/prettier-config/README.md
  • utils/prettier-config/package.json
  • utils/prettier-config/src/prettier-config.json
  • utils/renovate.json
  • utils/rollup-plugin/package.json
  • utils/rollup-plugin/rollup.config.mjs
  • utils/rollup-plugin/src/buildMap.ts
  • utils/rollup-plugin/src/config/createConfig.ts
  • utils/rollup-plugin/src/config/createSingleConfig.ts
  • utils/rollup-plugin/src/config/entry.ts
  • utils/rollup-plugin/src/config/externals.ts
  • utils/rollup-plugin/src/createParticlesBuild.ts
  • utils/rollup-plugin/src/index.ts
  • utils/rollup-plugin/src/types.ts
  • utils/rollup-plugin/tsconfig.json
  • utils/tsconfig/.gitignore
  • utils/tsconfig/CHANGELOG.md
  • utils/tsconfig/LICENSE
  • utils/tsconfig/README.md
  • utils/tsconfig/package.json
  • utils/tsconfig/src/tsconfig.base.json
  • utils/tsconfig/src/tsconfig.browser.json
  • utils/tsconfig/src/tsconfig.json
  • utils/tsconfig/src/tsconfig.module.json
  • utils/tsconfig/src/tsconfig.types.json
  • utils/webpack-config/.browserslistrc
  • utils/webpack-config/.gitignore
  • utils/webpack-config/CHANGELOG.md
  • utils/webpack-config/LICENSE
  • utils/webpack-config/README.md
  • utils/webpack-config/eslint.config.js
  • utils/webpack-config/package.json
  • utils/webpack-config/src/bundles/buildBundle.ts
  • utils/webpack-config/src/common/ExternalData.ts
  • utils/webpack-config/src/common/getConfig.ts
  • utils/webpack-config/src/common/getEntry.ts
  • utils/webpack-config/src/common/getExternals.ts
  • utils/webpack-config/src/effects/buildEffect.ts
  • utils/webpack-config/src/engine/buildEngine.ts
  • utils/webpack-config/src/interactions/buildInteraction.ts
  • utils/webpack-config/src/interactions/external/buildInteractionExternal.ts
  • utils/webpack-config/src/interactions/particles/buildInteractionParticles.ts
  • utils/webpack-config/src/palettes/buildPalette.ts
  • utils/webpack-config/src/paths/buildPath.ts
  • utils/webpack-config/src/plugins/buildPlugin.ts
  • utils/webpack-config/src/plugins/easings/buildEasing.ts
  • utils/webpack-config/src/plugins/emittersShapes/buildEmittersShape.ts
  • utils/webpack-config/src/plugins/exports/buildExport.ts
  • utils/webpack-config/src/presets/buildPreset.ts
  • utils/webpack-config/src/shapes/buildShape.ts
  • utils/webpack-config/src/templates/buildTemplate.ts
  • utils/webpack-config/src/updaters/buildUpdater.ts
  • utils/webpack-config/src/webpack-tsparticles.ts
  • utils/webpack-config/tsconfig.json

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR introduces Nx monorepo support to the tsParticles CLI build system. It splits the bundler into webpack/rollup variants, extracts build steps into standalone commands (eslint, prettier, tsc), creates an Nx plugin to recognize these commands as canonical targets, refactors the build orchestrator to try Nx execution first then fall back to legacy package.json scripts, updates ESLint to ^10.3.0 across packages, adds dependency-cruiser configurations, and refreshes documentation.

Changes

Core Build System & Nx Integration

Layer / File(s) Summary
Type & Interface Definitions
commands/build/src/nx-types.ts, commands/build/src/build-options.ts
New types for Nx command execution (PackageManager, NxCommand, NxContext, NxTargetPlan) and build options interface with feature flags and paths.
Nx Workspace Discovery & Execution
commands/build/src/nx-context.ts, commands/build/src/nx-targets.ts, commands/build/src/nx-plan.ts
Modules that locate Nx workspace root, resolve project context from package paths, define canonical target conventions, and plan which Nx targets to execute based on build options.
Nx Plugin Implementation
packages/nx-plugin/src/canonical-targets.ts, packages/nx-plugin/src/create-nodes.ts, packages/nx-plugin/src/index.ts
Nx plugin that augments workspace packages with canonical build-step target aliases (clean, prettify, lint, tsc, bundle:webpack, bundle:rollup, distfiles) by inspecting package.json scripts and creating nx:run-script executor targets.
New Modular Build Commands
commands/build-eslint/, commands/build-prettier/, commands/build-tsc/, commands/build-bundle-webpack/, commands/build-bundle-rollup/
Five new/split CLI commands with their own entry points, utility functions, package.json manifests, ESLint configs, TypeScript configs, and Renovate configs. Each registers a Commander subcommand (eslint, prettier, tsc, bundle:webpack, bundle:rollup).
Legacy Build Runner
commands/build/src/legacy-runner.ts
Fallback build orchestration that parses package.json scripts and runs them sequentially via the detected package manager when Nx is unavailable or skipped.
Nx Build Runner
commands/build/src/nx-runner.ts
Orchestrator that detects Nx task context, resolves Nx projects, plans target execution, and runs targets via nx run-many when Nx workspace is detected.
Refactored Main Build Command
commands/build/src/build.ts
Updated entrypoint that registers all subcommands (webpack/rollup/eslint/prettier/tsc/etc.), adds --nx flag and split bundle flags, constructs BuildExecutionOptions, and delegates to tryRunNxBuild or runLegacyBuild.
Build Command Tests & Config
commands/build/tests/nx-plan.test.ts, commands/build/vitest.config.ts
Vitest tests validating target planning logic across Nx alias availability and option combinations, plus Vitest configuration.
Workspace Config & Scripts
nx.json, package.json, tsconfig.json, root tsconfig.json
Updated Nx config to register the Nx plugin, root scripts changed to use pnpm nx run-many ... and new tasks (build:affected, lint, show:projects), and base TypeScript config added.
Documentation
README.md, commands/build/README.md, packages/nx-plugin/README.md, command READMEs
Updated guides describing Nx-aware build execution, canonical target conventions, fallback behavior, and usage examples.
Logging Standardization & Dependency Updates
commands/build-*/src/*.ts, package.json files
Replaced console.log with console.info for progress logging, bumped eslint to ^10.3.0 across all command packages, added dependency-cruiser configs (.dependency-cruiser.cjs) for circular dependency analysis.

Sequence Diagram

sequenceDiagram
    participant User
    participant Build as build<br/>(main cmd)
    participant NxDetect as Nx Detection
    participant NxExec as Nx Executor
    participant LegacyExec as Legacy<br/>Executor
    
    User->>Build: pnpm tsparticles-cli build [--nx]
    
    alt --nx flag or auto-detection
        Build->>NxDetect: resolveNxContext(basePath)
        NxDetect-->>Build: NxContext or undefined
        
        rect rgba(100, 200, 100, 0.5)
            Build->>Build: createNxTargetPlan(targets, options)
            Build-->>Build: NxTargetPlan { targets, missingSteps }
        end
        
        alt Nx context found & plan valid
            Build->>NxExec: tryRunNxBuild(options)
            NxExec->>NxExec: runNxTarget() for each target
            NxExec-->>Build: true
            Build-->>User: ✓ Build completed via Nx
        else No Nx or missing targets
            Build->>LegacyExec: runLegacyBuild(options)
            rect rgba(100, 100, 200, 0.5)
                LegacyExec->>LegacyExec: Parse package.json scripts
                LegacyExec->>LegacyExec: Run selected scripts sequentially
            end
            LegacyExec-->>Build: success/failure
            Build-->>User: ✓ Build completed (legacy)
        end
    else --legacy flag
        Build->>LegacyExec: runLegacyBuild(options)
        LegacyExec-->>Build: result
        Build-->>User: ✓ Build completed (legacy)
    end
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

  • 3.2.1 #102: Modifies the same build CLI codepaths, adding silent flags and changing logging/command signatures (e.g., build.ts, build-prettier, build-eslint, build-tsc, bundle functions).
  • 3.1.1 #89: Adds and adopts dependency-cruiser configurations across the build system, replacing prior circular-deps tooling.
  • monorepo wip #207: Modifies the monorepo build/CLI surface (commands/build, bundle packages, Nx plugin, nx.json, package manifests), introducing Nx-aware build targets and command modules.

Poem

🐰 Hops into Nx with glee,
Bundling split for webpack and rollup spree,
Legacy scripts still work just fine,
Console logs switched to info—all in line!
A monorepo made brighter, watch it grow! 🌱

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented May 1, 2026

View your CI Pipeline Execution ↗ for commit 7883212

Command Status Duration Result
nx run-many -t build:ci ❌ Failed 1m 11s View ↗

☁️ Nx Cloud last updated this comment at 2026-05-03 15:55:14 UTC

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces an internal Nx plugin for the tsParticles CLI monorepo and updates the build tooling to expose/consume canonical Nx targets (including separate Webpack vs Rollup bundle targets). It also adds new build subcommand packages and Vitest coverage around the new Nx-target planning.

Changes:

  • Add @tsparticles/cli-nx-plugin to infer canonical Nx target aliases from existing package.json scripts for commands/* and packages/*.
  • Refactor commands/build to plan and run build steps as Nx targets (with new target convention mapping and tests).
  • Add/build-split packages for Rollup bundling and for build steps like ESLint / Prettier / TSC, plus workspace script updates.

Reviewed changes

Copilot reviewed 59 out of 64 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
tsconfig.json Adds a root TypeScript configuration for the workspace.
pnpm-lock.yaml Updates lockfile for new workspace packages and dependencies (Nx plugin + rollup tooling).
packages/nx-plugin/vitest.config.ts Adds Vitest config for the Nx plugin package tests.
packages/nx-plugin/tsconfig.json Adds TS project config for the Nx plugin package.
packages/nx-plugin/tests/create-nodes.test.ts Adds tests for canonical alias generation and workspace package detection.
packages/nx-plugin/src/tsconfig.json Adds src-level TS config for building plugin sources.
packages/nx-plugin/src/index.ts Adds Nx plugin entrypoint/default export and re-export(s).
packages/nx-plugin/src/create-nodes.ts Implements createNodesV2 to augment Nx projects with canonical alias targets.
packages/nx-plugin/src/canonical-targets.ts Defines canonical alias mapping and helpers for generating Nx targets.
packages/nx-plugin/project.json Adds an Nx project config for the plugin package.
packages/nx-plugin/package.json Adds package metadata/scripts/deps for the Nx plugin package.
packages/nx-plugin/eslint.config.js Adds ESLint config for the Nx plugin package.
packages/nx-plugin/README.md Documents plugin behavior and canonical aliases.
package.json Updates root scripts to invoke Nx via pnpm nx ... and adds common workspace commands.
nx.json Registers @tsparticles/cli-nx-plugin in Nx plugins configuration.
commands/build/vitest.config.ts Adds Vitest config for commands/build tests.
commands/build/tests/nx-plan.test.ts Adds tests verifying canonical target selection and fallbacks.
commands/build/src/nx-types.ts Adds shared types for Nx detection/target planning.
commands/build/src/nx-targets.ts Defines canonical target conventions and target picking helper.
commands/build/src/nx-runner.ts Adds execution layer for running resolved Nx targets.
commands/build/src/nx-plan.ts Adds logic to build a target execution plan from CLI flags + available Nx targets.
commands/build/src/nx-context.ts Adds logic to detect Nx workspace/project context and run nx show .../targets.
commands/build/src/build.ts Refactors build command to construct Nx plan and execute targets; adds Rollup + new subcommands.
commands/build/src/build-options.ts Adds a typed options object for Nx planning/execution.
commands/build/package.json Adds vitest scripts and new build-step command dependencies (rollup, eslint, prettier, tsc).
commands/build/README.md Documents Nx-aware build mode and canonical target conventions.
commands/build-tsc/tsconfig.json Adds TS config for the new build-tsc package.
commands/build-tsc/src/utils.ts Implements TypeScript build utility for multiple compile targets.
commands/build-tsc/src/tsconfig.json Adds src-level TS config for build-tsc.
commands/build-tsc/src/tsc.ts Adds the CLI subcommand wrapper for the TSC build step.
commands/build-tsc/renovate.json Adds Renovate config for the build-tsc package.
commands/build-tsc/package.json Adds new @tsparticles/cli-command-build-tsc package metadata and deps.
commands/build-tsc/eslint.config.js Adds ESLint config for the build-tsc package.
commands/build-tsc/README.md Adds README for the build-tsc package.
commands/build-tsc/.dependency-cruiser.cjs Adds dependency-cruiser config for the build-tsc package.
commands/build-prettier/tsconfig.json Adds TS config for the new build-prettier package.
commands/build-prettier/src/utils.ts Implements Prettier formatting/check helpers.
commands/build-prettier/src/tsconfig.json Adds src-level TS config for build-prettier.
commands/build-prettier/src/prettier.ts Adds the CLI subcommand wrapper for the Prettier step.
commands/build-prettier/renovate.json Adds Renovate config for the build-prettier package.
commands/build-prettier/package.json Adds new @tsparticles/cli-command-build-prettier package metadata and deps.
commands/build-prettier/eslint.config.js Adds ESLint config for the build-prettier package.
commands/build-prettier/README.md Adds README for the build-prettier package.
commands/build-prettier/.dependency-cruiser.cjs Adds dependency-cruiser config for the build-prettier package.
commands/build-eslint/tsconfig.json Adds TS config for the new build-eslint package.
commands/build-eslint/src/utils.ts Implements ESLint runner helper.
commands/build-eslint/src/tsconfig.json Adds src-level TS config for build-eslint.
commands/build-eslint/src/eslint.ts Adds the CLI subcommand wrapper for the ESLint step.
commands/build-eslint/renovate.json Adds Renovate config for the build-eslint package.
commands/build-eslint/package.json Adds new @tsparticles/cli-command-build-eslint package metadata and deps.
commands/build-eslint/eslint.config.js Adds ESLint config for the build-eslint package.
commands/build-eslint/README.md Adds README for the build-eslint package.
commands/build-eslint/.dependency-cruiser.cjs Adds dependency-cruiser config for the build-eslint package.
commands/build-bundle/src/bundle.ts Renames bundle subcommand to bundle:webpack and updates messages.
commands/build-bundle-rollup/tsconfig.json Adds TS config for the new build-bundle-rollup package.
commands/build-bundle-rollup/src/utils.ts Implements Rollup config discovery + bundling via @tsparticles/rollup-plugin.
commands/build-bundle-rollup/src/tsconfig.json Adds src-level TS config for build-bundle-rollup.
commands/build-bundle-rollup/src/bundle-rollup.ts Adds the CLI subcommand wrapper for Rollup bundling.
commands/build-bundle-rollup/renovate.json Adds Renovate config for build-bundle-rollup.
commands/build-bundle-rollup/package.json Adds new @tsparticles/cli-command-build-bundle-rollup package metadata and deps.
commands/build-bundle-rollup/eslint.config.js Adds ESLint config for build-bundle-rollup.
commands/build-bundle-rollup/README.md Adds README for the Rollup bundle command package.
commands/build-bundle-rollup/.dependency-cruiser.cjs Reuses dependency-cruiser config from build-bundle.
README.md Updates root documentation for Nx workspace usage and developer commands.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +5 to +15
"targets": {
"build": {
"options": {
"main": "packages/nx-plugin/src/index.ts"
}
},
"build:ci": {
"options": {
"main": "packages/nx-plugin/src/index.ts"
}
}
Comment on lines +20 to +35
prettierCommand.action(async (argPath: string) => {
const opts = prettierCommand.opts(),
ci = !!opts["ci"],
silentOpt = opts["silent"] as string | boolean,
silent = silentOpt === "false" ? false : !!silentOpt || ci,
basePath = process.cwd();

if (!existsSync(basePath)) {
throw new Error("Provided path does not exist");
}

const srcPath = path.join(basePath, argPath);

if (!existsSync(srcPath)) {
throw new Error("Provided path does not exist");
}
Comment on lines +25 to +28
throw new Error("Dist files build failed");
}

console.log("Bundle finished successfully!");
Comment thread README.md
Comment on lines +51 to +65
### Build in an Nx workspace

The `build` command can delegate to Nx targets when it detects an Nx workspace.

```bash
tsparticles-cli build --nx
tsparticles-cli build --nx --clean --lint --tsc
tsparticles-cli build --legacy
pnpm nx run @tsparticles/cli-command-build:tsc
```

- `--nx`: forces Nx-target execution when required targets exist
- `--legacy`: disables Nx-aware mode and runs the original in-process pipeline
- default behavior in this workspace: with no granular flags, `build`/`build:ci` Nx aggregate targets are preferred when available

Comment thread README.md Outdated
- `--legacy`: disables Nx-aware mode and runs the original in-process pipeline
- default behavior in this workspace: with no granular flags, `build`/`build:ci` Nx aggregate targets are preferred when available

Inside this repository, the local plugin `@tsparticles/cli-nx-plugin` augments package projects under `commands/*` and `packages/*` with canonical aliases like `clean`, `prettify`, `prettify:ci`, `tsc`, `bundle`, and `distfiles`.
Comment thread commands/build-tsc/src/tsc.ts Outdated
Comment on lines +5 to +7
const tscCommand = new Command("distfiles");

tscCommand.description("Build the TypeScript files for tsParticles library");
Comment on lines +30 to +35
if (!(await buildTS(basePath, silent))) {
throw new Error("Dist files build failed");
}

console.log("Bundle finished successfully!");
});
Comment thread commands/build/src/build.ts Outdated
Comment on lines +50 to +86
buildCommand.option("-t, --tsc", "Build the library using TypeScript", false);
buildCommand.option("--nx", "Prefer running Nx targets when available", false);

buildCommand.argument("[path]", `Path to the project root folder, default is "src"`, "src");

buildCommand.action(async (argPath: string) => {
buildCommand.action((argPath: string) => {
const opts = buildCommand.opts(),
ci = !!opts["ci"],
all =
!!opts["all"] ||
(!opts["bundle"] &&
(!opts["bundleWebpack"] &&
!opts["bundleRollup"] &&
!opts["clean"] &&
!opts["circularDeps"] &&
!opts["dist"] &&
!opts["lint"] &&
!opts["prettify"] &&
!opts["tsc"]),
doBundle = all || !!opts["bundle"],
circularDeps = all || !!opts["circularDeps"],
clean = all || !!opts["clean"],
distfiles = all || !!opts["dist"],
doLint = all || !!opts["lint"],
prettier = all || !!opts["prettify"],
tsc = all || !!opts["tsc"],
silentOpt = opts["silent"] as string | boolean,
silent = silentOpt === "false" ? false : !!silentOpt || ci,
basePath = process.cwd(),
{ getDistStats } = await import("@tsparticles/cli-command-build-diststats"),
oldStats = await getDistStats(basePath);

if (clean) {
const { clearDist } = await import("@tsparticles/cli-command-build-clear");

await clearDist(basePath, silent);
}

const srcPath = path.join(basePath, argPath);

if (!existsSync(srcPath)) {
throw new Error("Provided path does not exist");
}

let canContinue = true;

if (prettier) {
const { prettifySrc } = await import("./build-prettier.js");

canContinue = await prettifySrc(basePath, srcPath, ci, silent);
}

if (canContinue && doLint) {
const { lint } = await import("./build-eslint.js");

canContinue = await lint(ci, silent);
}

if (canContinue && (tsc || circularDeps)) {
const checks: Promise<boolean>[] = [];

if (tsc) {
checks.push(import("./build-tsc.js").then(({ buildTS }) => buildTS(basePath, silent)));
}

if (circularDeps) {
checks.push(
import("@tsparticles/cli-command-build-circular-deps").then(({ circularDeps }) =>
circularDeps(basePath, silent),
),
);
}

canContinue = (await Promise.all(checks)).every(result => result);
}

if (canContinue && doBundle) {
const { bundle } = await import("@tsparticles/cli-command-build-bundle");

canContinue = await bundle(basePath, silent);
}

if (canContinue && prettier) {
const { prettifyReadme, prettifyPackageJson, prettifyPackageDistJson } = await import("./build-prettier.js");

canContinue =
(await prettifyReadme(basePath, ci, silent)) &&
(await prettifyPackageJson(basePath, ci, silent)) &&
(await prettifyPackageDistJson(basePath, ci, silent));
}

if (canContinue && distfiles) {
const { buildDistFiles } = await import("@tsparticles/cli-command-build-distfiles");

canContinue = await buildDistFiles(basePath, silent);
}

if (!canContinue) {
throw new Error("Build failed");
}

let texts: string[] = [],
outputFunc = console.info;

if (!silent) {
const newStats = await getDistStats(basePath),
diffSize = newStats.totalSize - oldStats.totalSize,
bundleDiffSize = newStats.bundleSize - oldStats.bundleSize,
minSize = 0,
bundleSizeIncreased = bundleDiffSize > minSize,
bundleSizeIncreasedText = bundleSizeIncreased ? "increased" : "decreased",
diffSizeIncreasedText = diffSize > minSize ? "increased" : "decreased";

outputFunc = bundleSizeIncreased ? console.warn : console.info;

texts = [
bundleDiffSize
? `Bundle size ${bundleSizeIncreasedText} from ${oldStats.bundleSize.toString()} to ${newStats.bundleSize.toString()} (${Math.abs(bundleDiffSize).toString()}B)`
: "Bundle size unchanged",
diffSize
? `Size ${diffSizeIncreasedText} from ${oldStats.totalSize.toString()} to ${newStats.totalSize.toString()} (${Math.abs(diffSize).toString()}B)`
: "Size unchanged",
`Files count changed from ${oldStats.totalFiles.toString()} to ${newStats.totalFiles.toString()} (${(
newStats.totalFiles - oldStats.totalFiles
).toString()})`,
`Folders count changed from ${oldStats.totalFolders.toString()} to ${newStats.totalFolders.toString()} (${(
newStats.totalFolders - oldStats.totalFolders
).toString()})`,
];
}

console.log("Build finished successfully!");

if (!silent) {
for (const text of texts) {
outputFunc(text);
}
commandOptions: BuildExecutionOptions = {
all,
argPath,
basePath: process.cwd(),
ci: !!opts["ci"],
circularDeps: all || !!opts["circularDeps"],
clean: all || !!opts["clean"],
distfiles: all || !!opts["dist"],
doBundleRollup: !!opts["bundleRollup"],
doBundleWebpack: all || !!opts["bundleWebpack"],
doLint: all || !!opts["lint"],
prettier: all || !!opts["prettify"],
silent: silentOpt === "false" ? false : !!silentOpt || !!opts["ci"],
tsc: all || !!opts["tsc"],
useNx: !!opts["nx"],
};

if (!tryRunNxBuild(commandOptions)) {
throw new Error("Nx build execution is required, but it cannot run inside an active Nx task context.");
@@ -2,9 +2,9 @@ import { Command } from "commander";
import { bundle } from "./utils.js";
import { existsSync } from "node:fs";

const bundleCommand = new Command("bundle");
const bundleCommand = new Command("bundle:webpack");

Comment thread commands/build-prettier/src/prettier.ts Outdated
Comment on lines +47 to +50
throw new Error("Dist files build failed");
}

console.log("Bundle finished successfully!");
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: 13

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (14)
commands/build-tsc/README.md-10-23 (1)

10-23: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Same wrong package name as commands/build-eslint/README.md.

All install examples use @particlesjs/cli instead of @tsparticles/cli. The same fix described in the build-eslint README comment applies here.

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

In `@commands/build-tsc/README.md` around lines 10 - 23, Replace the incorrect
package name '@particlesjs/cli' with the correct '@tsparticles/cli' in this
README's install examples (the npm, Yarn, and PNPM sections); search for the
literal string '@particlesjs/cli' and update each occurrence to
'@tsparticles/cli' so all three install command examples reflect the correct
package.
commands/build-eslint/README.md-10-23 (1)

10-23: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Wrong package name in install commands.

All install examples reference @particlesjs/cli, but the package is published as @tsparticles/cli (per packages/cli/package.json line 2). Running npm install -g @particlesjs/cli`` would install a different or non-existent package.

Additionally, the content is an exact copy of the top-level CLI README — it shows only generic build and create preset commands, with nothing specific to the build-eslint subcommand this package implements.

📝 Proposed fix for package name
 ### NPM
 
 ```bash
-npm install -g `@particlesjs/cli`
+npm install -g `@tsparticles/cli`

Yarn

-yarn global add `@particlesjs/cli`
+yarn global add `@tsparticles/cli`

PNPM

-pnpm global add `@particlesjs/cli`
+pnpm global add `@tsparticles/cli`
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @commands/build-eslint/README.md around lines 10 - 23, The README for the
build-eslint command incorrectly references the published CLI package name and
contains generic top-level CLI docs; update the install examples to use the
correct package name "@tsparticles/cli" (replace all occurrences of
"@particlesjs/cli" in the npm/yarn/pnpm install lines) and replace the copied
top-level CLI content with focused documentation for this package's subcommand
(describe usage of the build-eslint command, example invocations like
"tsparticles build-eslint [--fix] [--config ]", brief explanation
of options and expected outputs, and any package-specific notes) so the README
documents the actual build-eslint behavior rather than generic CLI commands.


</details>

</blockquote></details>
<details>
<summary>commands/build-bundle-rollup/tsconfig.json-46-46 (1)</summary><blockquote>

`46-46`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**`skipLibCheck: true` is absent in `commands/build-eslint/tsconfig.json` but present here**

This inconsistency means library declaration files are type-checked in `build-eslint` but skipped for `build-bundle-rollup`. If added to work around type incompatibilities in rollup's declaration files, add a comment explaining the reason. If added unintentionally, consider removing it to keep strict type checking uniform across packages.

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @commands/build-bundle-rollup/tsconfig.json at line 46, The tsconfig key
"skipLibCheck" is present in the build-bundle-rollup tsconfig but absent in the
build-eslint tsconfig, causing inconsistent library declaration checking; either
remove "skipLibCheck": true from the build-bundle-rollup tsconfig to keep strict
checks consistent, or if it was added intentionally to work around incompatible
third-party declaration files, add an inline comment next to the "skipLibCheck"
entry explaining the specific issue and why the exception is required so
maintainers understand the rationale; update the corresponding tsconfig so both
packages follow the chosen approach.


</details>

</blockquote></details>
<details>
<summary>commands/build-eslint/tsconfig.json-3-4 (1)</summary><blockquote>

`3-4`: _⚠️ Potential issue_ | _🟡 Minor_ | _⚡ Quick win_

**`outDir` and `rootDir` both set to `"."` with `composite: true`**

With `include: ["src/**/*"]` and `outDir: "."`, running `tsc -p tsconfig.json` directly (or `tsc --build` on this project) emits `.js`/`.d.ts` files alongside the `.ts` sources under `src/`. This is the established pattern in this codebase where the parent config is a template and actual compilation flows through `src/tsconfig.json`; however, the parent's `composite: true` makes it a legitimate build target in `--build` mode, so its own `outDir` should point somewhere clean (e.g., `"dist"` or at minimum a separate directory from the sources).

This pattern is repeated identically in `packages/nx-plugin/tsconfig.json` and `commands/build-bundle-rollup/tsconfig.json`. The same-directory `outDir`/`rootDir` also means TypeScript won't be able to distinguish emitted declarations from source files.



<details>
<summary>🔧 Proposed fix</summary>

```diff
-    "rootDir": ".",
-    "outDir": ".",
+    "rootDir": ".",
+    "outDir": "dist",

Or alternatively, since this tsconfig is only a base template and never a direct build target, suppress its compilation entirely:

+  "files": [],
   "include": [
     "src/**/*"
   ]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-eslint/tsconfig.json` around lines 3 - 4, The tsconfig has
composite: true with rootDir and outDir both set to ".", causing emitted
.js/.d.ts to land next to sources; update the tsconfig to point outDir to a
separate directory (e.g., change "outDir" from "." to "dist") so emitted files
are isolated (apply the same change to packages/nx-plugin/tsconfig.json and
commands/build-bundle-rollup/tsconfig.json), or if this file is only a template,
remove/switch off composite or add a noEmit flag to prevent it being a build
target—adjust the "composite", "outDir", or "noEmit" keys accordingly to resolve
the conflict.
commands/build-bundle-rollup/package.json-3-3 (1)

3-3: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Package version 3.4.7 is behind its own workspace sibling dependency floor of 3.4.14.

All @tsparticles/* siblings require ^3.4.14, yet this package declares version 3.4.7. In a pnpm workspace the lockfile resolves correctly, but publishing this package with a lower version than its required sibling minima is confusing and may break consumers who install it outside the workspace. Bump to at least 3.4.14 to align with the rest of the monorepo.

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

In `@commands/build-bundle-rollup/package.json` at line 3, The package.json
"version" field is lower than the monorepo sibling floor; update the "version"
value from 3.4.7 to at least 3.4.14 (matching the `@tsparticles/`* siblings'
^3.4.14 requirement), commit the change, and ensure any published metadata or
release scripts that read the package.json "version" are consistent with this
bump.
commands/build-bundle/src/utils.ts-41-51 (1)

41-51: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing return after reject() lets execution fall through to resolve(true).

When stats.hasErrors() is true, reject() is called but control continues into the warnings check and then unconditionally calls resolve(true). JavaScript Promise semantics ensure the first settlement wins (so the Promise is correctly rejected), but the fallthrough is misleading: warnings are still logged for error builds and the dead resolve(true) call is confusing. Add a return after the reject() call.

🐛 Proposed fix
         if (stats.hasErrors()) {
           console.error(statsInfo.errors);
 
-          reject(new Error(statsInfo.errors?.map(error => error.message).join("\n")));
+          reject(new Error(statsInfo.errors?.map(error => error.message).join("\n")));
+          return;
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-bundle/src/utils.ts` around lines 41 - 51, The error-handling
branch inside the stats handling (the block checking stats.hasErrors())
currently calls reject(new Error(...)) but falls through to the warnings check
and the final resolve(true); add a return immediately after the reject(...) in
that stats.hasErrors() branch (the same block that logs
console.error(statsInfo.errors) and calls reject(new
Error(statsInfo.errors?.map(...).join("\n")))) so execution stops there and
doesn't log warnings or call resolve(true) for errored builds.
commands/build-tsc/src/tsc.ts-34-34 (1)

34-34: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Stale "Bundle" success message in a TypeScript compilation command.

console.info("Bundle finished successfully!") is a copy-over from the original bundleCommand. Replace it with a message appropriate to TypeScript compilation.

🐛 Proposed fix
-  console.info("Bundle finished successfully!");
+  console.info("TypeScript compilation finished successfully!");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-tsc/src/tsc.ts` at line 34, Update the stale console.info
message in the TypeScript compilation command: replace the current
console.info("Bundle finished successfully!") call with a message that reflects
TypeScript compilation (for example use "TypeScript compilation completed
successfully" or similar) so the log no longer references "Bundle"; locate the
console.info call in the tsc command implementation and change only the string
content.
commands/build-eslint/src/eslint.ts-24-29 (1)

24-29: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Copy-paste error: wrong error message, wrong success message, and console.log instead of console.info

Three issues in the action handler:

  1. Line 25: "Dist files build failed" was copied from a distfiles command — should reflect an ESLint failure (e.g. "ESLint check failed").
  2. Line 28: "Bundle finished successfully!" describes a bundle step, not an ESLint run.
  3. Line 28: console.log should be console.info to match the PR's unified logging convention across all commands.
🐛 Proposed fix
   if (!(await lint(ci, silent))) {
-    throw new Error("Dist files build failed");
+    throw new Error("ESLint check failed");
   }
 
-  console.log("Bundle finished successfully!");
+  console.info("ESLint completed successfully!");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-eslint/src/eslint.ts` around lines 24 - 29, Replace the
incorrect messages and logger in the ESLint action handler: when lint(ci,
silent) returns false, throw a new Error with a correct message like "ESLint
check failed" instead of "Dist files build failed"; on success, replace the
misleading "Bundle finished successfully!" text with an ESLint-appropriate
message such as "ESLint check passed"; and use console.info instead of
console.log for the success message to follow the project's unified logging
convention (locate these changes around the lint(ci, silent) check and the
subsequent success log in commands/build-eslint/src/eslint.ts).
commands/build-bundle-rollup/src/utils.ts-63-73 (1)

63-73: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

bundleResult.generate() result is silently discarded

When output.file and output.dir are both absent, generate() computes the full in-memory bundle but the returned RollupOutput is thrown away — making the call a pure waste of CPU and memory. Likely a misconfiguration; consider throwing to surface it rather than silently no-op'ing.

💡 Proposed fix
 async function writeRollupOutput(
   bundleResult: Awaited<ReturnType<typeof rollup>>,
   output: OutputOptions,
 ): Promise<void> {
   if (output.file || output.dir) {
     await bundleResult.write(output);
 
     return;
   }
 
-  await bundleResult.generate(output);
+  throw new Error("Rollup output is missing both 'file' and 'dir'; cannot write bundle");
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-bundle-rollup/src/utils.ts` around lines 63 - 73, The
writeRollupOutput function currently calls bundleResult.generate(output) and
discards the result when neither output.file nor output.dir is set; change it to
surface this misconfiguration by validating the output options in
writeRollupOutput: if output.file or output.dir is present, call
bundleResult.write(output) and return as now; otherwise do NOT call
bundleResult.generate — instead throw a clear Error (from writeRollupOutput)
that includes context (e.g., stringify the output options) so callers see the
invalid configuration rather than wasting CPU/memory; reference
bundleResult.generate and the output parameter so the throw replaces the
discarded generate call.
commands/build-bundle/src/bundle-webpack.ts-26-28 (1)

26-28: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Same existsSync(process.cwd()) dead code as in bundle-rollup.ts

process.cwd() always points to an existing directory; this guard is unreachable and should be removed (same fix as in bundle-rollup.ts lines 26-28).

🧹 Proposed fix
   const opts = bundleWebpackCommand.opts(),
     ci = !!opts["ci"],
     silentOpt = opts["silent"] as string | boolean,
     silent = silentOpt === "false" ? false : !!silentOpt || ci,
     basePath = process.cwd();
 
-  if (!existsSync(basePath)) {
-    throw new Error("Provided path does not exist");
-  }
-
   if (!(await bundleWebpack(basePath, silent))) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-bundle/src/bundle-webpack.ts` around lines 26 - 28, Remove the
unreachable guard that checks filesystem existence for the current working
directory: delete the if (!existsSync(basePath)) throw new Error("Provided path
does not exist"); check (the one using existsSync/process.cwd()-derived
basePath) in bundle-webpack.ts; if you still need to validate a user-supplied
path, validate a real input path variable instead of process.cwd(), otherwise
simply remove the dead check referencing basePath/existsSync.
commands/build-prettier/src/prettier.ts-27-29 (1)

27-29: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

existsSync(process.cwd()) is always true — same dead code as in the bundle commands

Identical to the unreachable guard noted in bundle-rollup.ts. Remove to keep the logic clean.

🧹 Proposed fix
   const opts = prettierCommand.opts(),
     ci = !!opts["ci"],
     silentOpt = opts["silent"] as string | boolean,
     silent = silentOpt === "false" ? false : !!silentOpt || ci,
     basePath = process.cwd();
 
-  if (!existsSync(basePath)) {
-    throw new Error("Provided path does not exist");
-  }
-
   const srcPath = path.join(basePath, argPath);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-prettier/src/prettier.ts` around lines 27 - 29, Remove the
redundant reachability check that uses existsSync with the current working
directory pattern: delete the if-block that checks existsSync(basePath) and
throws new Error("Provided path does not exist"); instead rely on upstream
validation or let downstream fs calls surface errors—locate the conditional
using existsSync and the variable basePath in the module (e.g., the if
(!existsSync(basePath)) { throw new Error(...) } block) and remove it to match
the bundle-rollup.ts cleanup.
commands/build-bundle-rollup/src/bundle-rollup.ts-26-28 (1)

26-28: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

existsSync(process.cwd()) is always true — dead code

process.cwd() returns the directory the current process is running in, which is guaranteed to exist. This guard can never trigger and should be removed to avoid misleading future readers. The same unreachable check is also present in commands/build-bundle/src/bundle-webpack.ts (line 26) and commands/build-prettier/src/prettier.ts (line 27).

🧹 Proposed fix
   const opts = bundleRollupCommand.opts(),
     ci = !!opts["ci"],
     silentOpt = opts["silent"] as string | boolean,
     silent = silentOpt === "false" ? false : !!silentOpt || ci,
     basePath = process.cwd();
 
-  if (!existsSync(basePath)) {
-    throw new Error("Provided path does not exist");
-  }
-
   if (!(await bundleRollup(basePath, silent))) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-bundle-rollup/src/bundle-rollup.ts` around lines 26 - 28,
Remove the unreachable filesystem existence guard that calls
existsSync(process.cwd()) (and similar checks) — locate the early check in
bundle-rollup.ts (the if (!existsSync(basePath)) / existsSync(process.cwd())
pattern) and delete it from bundle-rollup.ts, bundle-webpack.ts, and
prettier.ts; if you intended to validate a user-provided path, replace the check
with a real validation against the user input variable (e.g., validate
`basePath` or the CLI path arg) and throw a clear error only when that
user-supplied path does not exist.
README.md-66-66 (1)

66-66: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

bundle alias is incorrect — the actual targets are bundle:rollup and bundle:webpack.

The test file at packages/nx-plugin/tests/create-nodes.test.ts (line 19-20) confirms the canonical aliases generated are bundle:rollup and bundle:webpack, not a single bundle target. Running pnpm nx run <pkg>:bundle will fail with "target not found".

📝 Proposed fix
-Inside this repository, the local plugin `@tsparticles/cli-nx-plugin` augments package projects under `commands/*` and `packages/*` with canonical aliases like `clean`, `prettify`, `prettify:ci`, `tsc`, `bundle`, and `distfiles`.
+Inside this repository, the local plugin `@tsparticles/cli-nx-plugin` augments package projects under `commands/*` and `packages/*` with canonical aliases like `clean`, `prettify`, `prettify:ci`, `tsc`, `bundle:webpack`, `bundle:rollup`, and `distfiles`.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 66, The README incorrectly lists a single `bundle` alias;
update the description to list the actual canonical targets `bundle:rollup` and
`bundle:webpack` (as asserted by the test `create-nodes.test.ts`), replacing
"bundle" with "bundle:rollup" and "bundle:webpack" so references to running
`pnpm nx run <pkg>:bundle` are not misleading and reflect the real targets.
commands/build/README.md-62-63 (1)

62-63: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Contradictory --nx flag description

Line 62 says --nx is "preserved for compatibility" and "the build flow is Nx-only," but line 63 says the "default behavior" is to resolve Nx targets. If --nx is merely a compatibility alias and the default already delegates to Nx, it's unclear when or why you would pass --nx at all. Additionally, the code block above (lines 58–59) shows --bundle-webpack and --bundle-rollup without --nx, which further muddies the distinction.

Consider clarifying whether --nx is a no-op (backward-compatible alias), an opt-in flag, or truly redundant given the new default behavior.

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

In `@commands/build/README.md` around lines 62 - 63, Clarify the README's
description of the --nx flag: state explicitly whether --nx is a no-op
backward-compatibility alias, an opt-in override, or redundant with the current
default behavior that resolves Nx targets; update the sentence mentioning `--nx`
to indicate its exact behavior (e.g., "preserved for compatibility (no-op
alias)" or "force Nx mode when other bundlers might be chosen") and adjust the
following "default behavior" line to remove ambiguity by saying "default:
resolves and runs Nx targets (so passing `--nx` is optional/for-compatibility)"
and add a short note explaining how `--bundle-webpack` and `--bundle-rollup`
interact with `--nx` (whether they override it or can be used concurrently).
🧹 Nitpick comments (13)
tsconfig.json (1)

1-10: ⚡ Quick win

Consider adding strict: true as a safe root default.

The three-level hierarchy (root → intermediate → src) means strict settings live at the intermediate level, which is fine. However, if a new command package ever extends this root directly and skips the intermediate config, it will silently compile without strict mode. Adding strict: true here is a no-op for correctly structured packages (they re-declare strict options at the intermediate level) but prevents the silent regression for any future package that skips the intermediate level.

🔒 Proposed addition
 {
   "compilerOptions": {
     "target": "ESNext",
     "module": "NodeNext",
     "moduleResolution": "NodeNext",
     "esModuleInterop": true,
     "allowSyntheticDefaultImports": true,
-    "types": ["node"]
+    "types": ["node"],
+    "strict": true
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tsconfig.json` around lines 1 - 10, Add "strict": true to the root tsconfig's
compilerOptions to enforce strict type checking by default; update the
tsconfig.json compilerOptions block (where "target", "module",
"moduleResolution", "esModuleInterop", "allowSyntheticDefaultImports", and
"types" are declared) to include strict: true so any package that extends the
root but skips the intermediate config still compiles with strict checks.
commands/build-bundle-rollup/renovate.json (1)

6-8: ⚡ Quick win

config:base is deprecated — use config:recommended instead.

config:base was renamed to config:recommended in Renovate v36. config:recommended is now Renovate's canonical default configuration. Renovate will raise its own config migration PR for this, but fixing it proactively avoids the noise. The same pattern appears in the sibling renovate.json files added in this PR (commands/build-eslint, commands/build-prettier, commands/build-tsc) — they should all be updated together.

⚙️ Proposed fix
 {
   "$schema": "https://docs.renovatebot.com/renovate-schema.json",
   "baseBranches": [
     "dev"
   ],
   "extends": [
-    "config:base"
+    "config:recommended"
   ]
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-bundle-rollup/renovate.json` around lines 6 - 8, Update the
Renovate preset from the deprecated "config:base" to "config:recommended": in
commands/build-bundle-rollup/renovate.json replace the extends entry
"config:base" with "config:recommended", and make the same change in the sibling
files commands/build-eslint/renovate.json,
commands/build-prettier/renovate.json, and commands/build-tsc/renovate.json so
all Renovate configs use the current canonical default.
commands/build-tsc/renovate.json (1)

6-8: ⚡ Quick win

config:base is deprecated — use config:recommended.

Same issue as commands/build-eslint/renovate.json: the Renovate config validator flags this as "Config migration necessary" and outputs the corrected config with "config:recommended".

♻️ Proposed fix
  "extends": [
-   "config:base"
+   "config:recommended"
  ]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-tsc/renovate.json` around lines 6 - 8, Replace the deprecated
Renovate preset by updating the "extends" entry that currently contains
"config:base" to use "config:recommended" instead; locate the "extends" array in
the renovate.json (the element with value "config:base") and change that string
to "config:recommended" so the config validator stops reporting "Config
migration necessary".
commands/build-eslint/renovate.json (1)

6-8: ⚡ Quick win

config:base is deprecated — use config:recommended instead.

The Renovate config validator explicitly warns "Config migration necessary" when config:base is present, showing that "config:base" should be replaced with "config:recommended". Renovate renamed config:base to config:recommended in v36.

♻️ Proposed fix
  "extends": [
-   "config:base"
+   "config:recommended"
  ]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-eslint/renovate.json` around lines 6 - 8, Replace the
deprecated Renovate preset by updating the "extends" entry that currently
contains "config:base" to "config:recommended"; locate the renovat e config's
"extends" array (the string "config:base") and substitute it with
"config:recommended" so the Renovate config validator no longer reports "Config
migration necessary".
commands/build-bundle-rollup/package.json (2)

36-59: ⚡ Quick win

Build/lint toolchain entries belong in devDependencies, not dependencies.

eslint, prettier, typescript, dependency-cruiser, eslint-config-prettier, eslint-plugin-jsdoc, eslint-plugin-prettier, eslint-plugin-tsdoc, typescript-eslint, and rimraf are development-time tools used to build and validate this package's own source. Placing them in dependencies causes every consumer that installs @tsparticles/cli-command-build-bundle-rollup to unnecessarily pull in the entire lint/compiler stack as transitive runtime dependencies.

♻️ Proposed reorganization
  "dependencies": {
    "@swc/core": "^1.15.32",
-   "@tsparticles/depcruise-config": "^3.4.14",
-   "@tsparticles/eslint-config": "^3.4.14",
-   "@tsparticles/prettier-config": "^3.4.14",
    "@tsparticles/rollup-plugin": "^3.4.14",
-   "@tsparticles/tsconfig": "^3.4.14",
-   "eslint": "^10.3.0",
-   "eslint-config-prettier": "^10.1.8",
-   "eslint-plugin-jsdoc": "^62.9.0",
-   "eslint-plugin-prettier": "^5.5.5",
-   "eslint-plugin-tsdoc": "^0.5.2",
    "klaw": "^4.1.0",
    "lookpath": "^1.2.3",
-   "dependency-cruiser": "^17.3.10",
    "path-scurry": "^2.0.2",
-   "prettier": "^3.8.3",
-   "prettier-plugin-multiline-arrays": "^4.1.7",
    "prompts": "^2.4.2",
-   "rimraf": "^6.1.3",
    "rollup": "^4.52.5",
-   "typescript": "^6.0.3",
-   "typescript-eslint": "^8.59.1"
  },
  "devDependencies": {
+   "@tsparticles/depcruise-config": "^3.4.14",
+   "@tsparticles/eslint-config": "^3.4.14",
+   "@tsparticles/prettier-config": "^3.4.14",
+   "@tsparticles/tsconfig": "^3.4.14",
    "@types/estree": "^1.0.8",
    "@types/klaw": "^3.0.7",
    "@types/node": "^25.6.0",
    "@types/prompts": "^2.4.9",
    "browserslist": "^4.28.2",
    "commander": "^14.0.3",
    "copyfiles": "^2.4.1",
    "cross-env": "^10.1.0",
+   "dependency-cruiser": "^17.3.10",
+   "eslint": "^10.3.0",
+   "eslint-config-prettier": "^10.1.8",
+   "eslint-plugin-jsdoc": "^62.9.0",
+   "eslint-plugin-prettier": "^5.5.5",
+   "eslint-plugin-tsdoc": "^0.5.2",
+   "prettier": "^3.8.3",
+   "prettier-plugin-multiline-arrays": "^4.1.7",
+   "rimraf": "^6.1.3",
    "ts-node": "^10.9.2",
+   "typescript": "^6.0.3",
+   "typescript-eslint": "^8.59.1",
    "vitest": "^4.1.5"
  },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-bundle-rollup/package.json` around lines 36 - 59, The
package.json currently lists build and lint tools under "dependencies" which
makes them runtime transitive deps; move the build/lint tool entries (eslint,
prettier, typescript, dependency-cruiser, eslint-config-prettier,
eslint-plugin-jsdoc, eslint-plugin-prettier, eslint-plugin-tsdoc,
typescript-eslint, rimraf, swc-loader, `@swc/core`,
prettier-plugin-multiline-arrays, and any other non-runtime tooling) from the
"dependencies" object into "devDependencies" so only true runtime libs (e.g.,
rollup, prompts, klaw, lookpath, path-scurry) remain in "dependencies"; update
package.json accordingly and run npm/yarn install to validate the lockfile
changes.

26-27: 💤 Low value

build:ts:cjs naming is misleading for an ESM package.

With "type": "module" set, TypeScript output is treated as ESM, not CJS. The :cjs suffix is confusing (it matches the pattern in the analogous webpack package but is semantically wrong here unless the tsconfig explicitly emits .cjs extensions).

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

In `@commands/build-bundle-rollup/package.json` around lines 26 - 27, The npm
script name build:ts:cjs is misleading for an ESM package; rename the script to
reflect ESM output (for example build:ts:esm) and update the dependent script
build:ts to call the new name, or alternatively remove the :cjs suffix and keep
a single build:ts script; adjust only the "build:ts" and "build:ts:cjs" script
entries in package.json so names match the actual ESM output from tsc.
commands/build-eslint/.dependency-cruiser.cjs (1)

1-382: ⚡ Quick win

Consider re-exporting the shared config rather than duplicating it.

This file is a verbatim copy of commands/build-prettier/.dependency-cruiser.cjs. The AI summary notes that commands/build-bundle-rollup/.dependency-cruiser.cjs already re-exports @tsparticles/depcruise-config instead of inlining the full template. Any future rule changes (e.g., adding a new forbidden pattern, adjusting severity) will need to be applied in both files manually.

♻️ Proposed refactor — re-export the shared config
-/** `@type` {import('dependency-cruiser').IConfiguration} */
-module.exports = {
-  forbidden: [
-    // ... (382 lines of duplicated config)
-  ],
-  options: { ... }
-};
-// generated: dependency-cruiser@17.3.7 on 2026-01-28T14:30:16.611Z
+const { default: baseConfig } = require("@tsparticles/depcruise-config");
+
+/** `@type` {import('dependency-cruiser').IConfiguration} */
+module.exports = {
+  ...baseConfig,
+  options: {
+    ...baseConfig.options,
+    tsConfig: { fileName: "src/tsconfig.json" },
+  },
+};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-eslint/.dependency-cruiser.cjs` around lines 1 - 382, This
file duplicates the full dependency-cruiser configuration inline instead of
re-exporting the shared config; replace the large module.exports object with a
single re-export of the shared config (i.e., set module.exports =
require('@tsparticles/depcruise-config') or import and export it) so future rule
changes live in one place; look for the top-level module.exports object in this
file and swap it to re-export the package named '@tsparticles/depcruise-config'.
commands/build-eslint/package.json (1)

5-5: build:ts:cjs script name is misleading — outputs ESM, not CommonJS.

The package is correctly configured for ESM: tsconfig.json uses "module": "NodeNext", which emits ESM modules. The "type": "module" declaration (line 5) and "main": "dist/eslint.js" (line 71) are compatible with this output and will work correctly at runtime.

However, the script name build:ts:cjs (line 26) is misleading. It suggests CommonJS output but actually compiles to ESM. Consider renaming it to build:ts or build:ts:esm for clarity.

Additionally, for modern ESM packages, consider using the "exports" field instead of "main" to provide explicit entry points and better tooling support.

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

In `@commands/build-eslint/package.json` at line 5, The npm script name
"build:ts:cjs" is misleading because the package is ESM ("type": "module" and
"main": "dist/eslint.js") and tsconfig emits ESM; rename the script
"build:ts:cjs" to a clearer name such as "build:ts" or "build:ts:esm" in
package.json and update any references (CI, docs, other npm scripts) to the old
name; optionally add an "exports" field alongside or in place of "main" to
explicitly declare entry points for modern ESM tooling.
packages/nx-plugin/package.json (1)

27-44: ⚡ Quick win

Dev-only tools belong in devDependencies, not dependencies

Only @nx/devkit is a genuine runtime dependency for this Nx plugin. Everything else (vitest, eslint, prettier, rimraf, typescript, typescript-eslint, all eslint-plugin-*, prettier-plugin-multiline-arrays, even nx itself) is only needed for linting, testing, and building. Although the package is "private": true and won't be published to npm, placing these in dependencies is semantically misleading and can lead to unintended installation overhead in workspace dependency hoisting.

🔧 Proposed reorganization
  "dependencies": {
    "@nx/devkit": "^22.7.1"
  },
  "devDependencies": {
    "@tsparticles/eslint-config": "^3.4.14",
    "@tsparticles/prettier-config": "^3.4.14",
    "@tsparticles/tsconfig": "^3.4.14",
+   "@types/node": "^25.6.0",
    "eslint": "^10.3.0",
    "eslint-config-prettier": "^10.1.8",
    "eslint-plugin-jsdoc": "^62.9.0",
    "eslint-plugin-prettier": "^5.5.5",
    "eslint-plugin-tsdoc": "^0.5.2",
    "nx": "22.7.1",
    "prettier": "^3.8.3",
    "prettier-plugin-multiline-arrays": "^4.1.7",
    "rimraf": "^6.1.3",
    "typescript": "^6.0.3",
    "typescript-eslint": "^8.59.1",
    "vitest": "^4.1.5"
- },
- "devDependencies": {
-   "@types/node": "^25.6.0"
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nx-plugin/package.json` around lines 27 - 44, The package.json
currently lists many dev-only tools under "dependencies"; move all non-runtime
packages into "devDependencies" and keep only true runtime deps (e.g.,
"@nx/devkit") in "dependencies". Specifically, remove "vitest", "eslint",
"eslint-config-prettier", "eslint-plugin-jsdoc", "eslint-plugin-prettier",
"eslint-plugin-tsdoc", "prettier", "prettier-plugin-multiline-arrays", "rimraf",
"typescript", "typescript-eslint", and "nx" from "dependencies" and add them
under "devDependencies" instead, ensuring package.json reflects that only
`@nx/devkit` remains as a runtime dependency.
commands/build/src/nx-context.ts (1)

186-208: 💤 Low value

Fallback project resolution may be slow for large workspaces.

This loop queries every remaining project via nx show project to find a match by root. In large Nx workspaces, this could result in many subprocess invocations. However, this is a reasonable fallback for edge cases where the project name differs from both the directory name and package.json name.

If performance becomes an issue, consider caching the full project list with roots in a single query using nx show projects --json --with-target or similar.

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

In `@commands/build/src/nx-context.ts` around lines 186 - 208, The fallback loop
that calls runNxJsonCommand for each project (iterating remainingProjectNames
and using toNxProjectDetails with "show project") is expensive for large
workspaces; replace it by making a single query that returns all projects/roots
(e.g., call runNxJsonCommand once with "show projects --json" or similar) and
build a map of projectName -> root/targets, then find the matching project by
comparing roots to absoluteBasePath; update the code that currently uses
remainingProjectNames/toNxProjectDetails to consult this cached map and return
the matched workspaceRoot/packageManager/projectName/targets instead of invoking
runNxJsonCommand per project.
commands/build/src/build.ts (1)

76-77: 💤 Low value

Asymmetric bundler behavior in --all mode.

When --all is set, doBundleWebpack becomes true but doBundleRollup remains false. This means --all triggers webpack bundling but not rollup bundling.

If this is intentional (webpack is the primary bundler), consider documenting this in the --all option description. If both bundlers should be included, doBundleRollup should also check all.

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

In `@commands/build/src/build.ts` around lines 76 - 77, The current logic sets
doBundleWebpack to all || !!opts["bundleWebpack"] but leaves doBundleRollup as
!!opts["bundleRollup"], causing asymmetric behavior when all is true; update the
doBundleRollup assignment to also respect the all flag (i.e., change
doBundleRollup to all || !!opts["bundleRollup"]) so both bundlers run under
--all, or if asymmetric behavior is intended, add a clear note to the --all
option documentation explaining that --all triggers only webpack (reference the
doBundleRollup and doBundleWebpack variables and the opts/all flags when making
the change).
packages/nx-plugin/src/canonical-targets.ts (2)

10-10: 💤 Low value

pnpmRunPrefix is hardcoded in target metadata.

metadata.runCommand is purely informational (shown in Nx Console), so this doesn't affect execution correctness. However, in a workspace that might ever be switched to npm or yarn, the hint would be misleading. Since the entire repo is pnpm-first and execution goes through nx:run-script regardless of what the metadata says, this is cosmetic only.

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

In `@packages/nx-plugin/src/canonical-targets.ts` at line 10, The metadata
currently hardcodes pnpmRunPrefix ("pnpm run") which makes metadata.runCommand
misleading for non-pnpm workspaces; update canonical-targets.ts to avoid the
hardcoded pnpm hint by either deriving the prefix from the detected package
manager or by using a generic hint (e.g., "npm run / yarn run / pnpm run" or
simply "run") and replace usages of pnpmRunPrefix in the code that sets
metadata.runCommand so the displayed hint is accurate regardless of package
manager; look for the pnpmRunPrefix constant and where metadata.runCommand is
built and change that logic accordingly.

101-109: 💤 Low value

isTsParticlesWorkspacePackage: logic is correct but hardcodes workspace topology.

The path matching implicitly encodes the two allowed top-level directories (commands/, packages/). If the monorepo ever gains a new top-level source directory, this function needs a matching update. Worth noting as a future maintenance touch-point; no change required now.

Also, normalizedPath.includes("/files/") requires a leading slash so it won't match a hypothetical root-level files/…/package.json path — but since such a path would also fail the startsWith check, there is no logical gap.

♻️ Optional: drive the allowlist from a constant to ease future extension
+const workspacePrefixes = ["commands/", "packages/"] as const;
+
 export function isTsParticlesWorkspacePackage(packageJsonPath: string): boolean {
   const normalizedPath = packageJsonPath.replaceAll("\\", "/");

   if (normalizedPath.includes("/files/")) {
     return false;
   }

-  return normalizedPath.startsWith("commands/") || normalizedPath.startsWith("packages/");
+  return workspacePrefixes.some(prefix => normalizedPath.startsWith(prefix));
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nx-plugin/src/canonical-targets.ts` around lines 101 - 109, The
function isTsParticlesWorkspacePackage hardcodes allowed top-level dirs; define
a constant array (e.g., CANONICAL_TOP_LEVEL_DIRS = ["commands/", "packages/"])
and replace the explicit startsWith checks with
CANONICAL_TOP_LEVEL_DIRS.some(dir => normalizedPath.startsWith(dir)); keep the
existing normalizedPath.includes("/files/") early-exit logic but move it above
the array check so the behavior stays identical; update the function
isTsParticlesWorkspacePackage to reference the new constant.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c4899e12-add2-4a6f-9e34-e6212ecf1499

📥 Commits

Reviewing files that changed from the base of the PR and between 99248e6 and 6e5e3d1.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (76)
  • README.md
  • commands/build-bundle-rollup/.dependency-cruiser.cjs
  • commands/build-bundle-rollup/README.md
  • commands/build-bundle-rollup/eslint.config.js
  • commands/build-bundle-rollup/package.json
  • commands/build-bundle-rollup/renovate.json
  • commands/build-bundle-rollup/src/bundle-rollup.ts
  • commands/build-bundle-rollup/src/tsconfig.json
  • commands/build-bundle-rollup/src/utils.ts
  • commands/build-bundle-rollup/tsconfig.json
  • commands/build-bundle/package.json
  • commands/build-bundle/src/bundle-webpack.ts
  • commands/build-bundle/src/utils.ts
  • commands/build-circular-deps/package.json
  • commands/build-circular-deps/src/circular-deps.ts
  • commands/build-circular-deps/src/utils.ts
  • commands/build-clear/package.json
  • commands/build-distfiles/package.json
  • commands/build-distfiles/src/distfiles.ts
  • commands/build-distfiles/src/utils.ts
  • commands/build-diststats/package.json
  • commands/build-eslint/.dependency-cruiser.cjs
  • commands/build-eslint/README.md
  • commands/build-eslint/eslint.config.js
  • commands/build-eslint/package.json
  • commands/build-eslint/renovate.json
  • commands/build-eslint/src/eslint.ts
  • commands/build-eslint/src/tsconfig.json
  • commands/build-eslint/src/utils.ts
  • commands/build-eslint/tsconfig.json
  • commands/build-prettier/.dependency-cruiser.cjs
  • commands/build-prettier/README.md
  • commands/build-prettier/eslint.config.js
  • commands/build-prettier/package.json
  • commands/build-prettier/renovate.json
  • commands/build-prettier/src/prettier.ts
  • commands/build-prettier/src/tsconfig.json
  • commands/build-prettier/src/utils.ts
  • commands/build-prettier/tsconfig.json
  • commands/build-tsc/.dependency-cruiser.cjs
  • commands/build-tsc/README.md
  • commands/build-tsc/eslint.config.js
  • commands/build-tsc/package.json
  • commands/build-tsc/renovate.json
  • commands/build-tsc/src/tsc.ts
  • commands/build-tsc/src/tsconfig.json
  • commands/build-tsc/src/utils.ts
  • commands/build-tsc/tsconfig.json
  • commands/build/README.md
  • commands/build/package.json
  • commands/build/src/build-options.ts
  • commands/build/src/build.ts
  • commands/build/src/nx-context.ts
  • commands/build/src/nx-plan.ts
  • commands/build/src/nx-runner.ts
  • commands/build/src/nx-targets.ts
  • commands/build/src/nx-types.ts
  • commands/build/tests/nx-plan.test.ts
  • commands/build/vitest.config.ts
  • commands/create-utils/package.json
  • commands/create/package.json
  • nx.json
  • package.json
  • packages/cli/package.json
  • packages/nx-plugin/README.md
  • packages/nx-plugin/eslint.config.js
  • packages/nx-plugin/package.json
  • packages/nx-plugin/project.json
  • packages/nx-plugin/src/canonical-targets.ts
  • packages/nx-plugin/src/create-nodes.ts
  • packages/nx-plugin/src/index.ts
  • packages/nx-plugin/src/tsconfig.json
  • packages/nx-plugin/tests/create-nodes.test.ts
  • packages/nx-plugin/tsconfig.json
  • packages/nx-plugin/vitest.config.ts
  • tsconfig.json

"prompts": "^2.4.2",
"rimraf": "^6.1.3",
"rollup": "^4.52.5",
"swc-loader": "^0.2.7",
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 | 🟠 Major | ⚡ Quick win

swc-loader is a webpack loader and has no place in the rollup-bundle package.

swc-loader is webpack-specific — it integrates SWC into webpack's loader pipeline and cannot be used by rollup. The rollup package already has @swc/core as a direct runtime dependency; for SWC in rollup you would need a rollup plugin (e.g. rollup-plugin-swc3), not a webpack loader. Remove swc-loader from this package.

🐛 Proposed fix
-    "swc-loader": "^0.2.7",
📝 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
"swc-loader": "^0.2.7",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-bundle-rollup/package.json` at line 57, Remove the
webpack-specific dependency "swc-loader" from the package.json of the rollup
bundle package because it's not usable with Rollup; instead ensure the package
uses `@swc/core` (already present) or a Rollup plugin such as rollup-plugin-swc3
when SWC transforms are needed in the Rollup build. Locate the "swc-loader"
entry in package.json and delete that dependency, and if build tooling requires
SWC during bundling, add or document the appropriate Rollup plugin (e.g.,
rollup-plugin-swc3) rather than a webpack loader.

Comment thread commands/build-bundle-rollup/src/tsconfig.json
Comment on lines +86 to +111
try {
const configs = await loadRollupConfig(basePath);

for (const config of configs) {
const bundleResult = await rollup(config);
let outputs: OutputOptions[] = [];

if (config.output) {
outputs = Array.isArray(config.output) ? config.output : [config.output];
}

if (outputs.length === emptyCount) {
throw new Error("Rollup config is missing output settings");
}

for (const output of outputs) {
await writeRollupOutput(bundleResult, output);
}

await bundleResult.close();
}
} catch (e) {
console.error(e);

return false;
}
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 | 🟠 Major | ⚡ Quick win

bundleResult leaks when an error is thrown before close()

If the outputs.length === 0 guard at line 98 throws, or if writeRollupOutput (line 102) rejects, execution jumps directly to the outer catch block, which only logs and returns falsebundleResult.close() is never called. Rollup's RollupBuild can hold open file handles/watchers, so this is a real resource leak for any error path.

🛡️ Proposed fix – wrap output processing in try/finally
     for (const config of configs) {
       const bundleResult = await rollup(config);
       let outputs: OutputOptions[] = [];
 
+      try {
         if (config.output) {
           outputs = Array.isArray(config.output) ? config.output : [config.output];
         }
 
-        if (outputs.length === emptyCount) {
+        if (outputs.length === 0) {
           throw new Error("Rollup config is missing output settings");
         }
 
         for (const output of outputs) {
           await writeRollupOutput(bundleResult, output);
         }
-
-      await bundleResult.close();
+      } finally {
+        await bundleResult.close();
+      }
     }
📝 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
try {
const configs = await loadRollupConfig(basePath);
for (const config of configs) {
const bundleResult = await rollup(config);
let outputs: OutputOptions[] = [];
if (config.output) {
outputs = Array.isArray(config.output) ? config.output : [config.output];
}
if (outputs.length === emptyCount) {
throw new Error("Rollup config is missing output settings");
}
for (const output of outputs) {
await writeRollupOutput(bundleResult, output);
}
await bundleResult.close();
}
} catch (e) {
console.error(e);
return false;
}
try {
const configs = await loadRollupConfig(basePath);
for (const config of configs) {
const bundleResult = await rollup(config);
let outputs: OutputOptions[] = [];
try {
if (config.output) {
outputs = Array.isArray(config.output) ? config.output : [config.output];
}
if (outputs.length === 0) {
throw new Error("Rollup config is missing output settings");
}
for (const output of outputs) {
await writeRollupOutput(bundleResult, output);
}
} finally {
await bundleResult.close();
}
}
} catch (e) {
console.error(e);
return false;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-bundle-rollup/src/utils.ts` around lines 86 - 111, When
iterating configs after loadRollupConfig, ensure RollupBuild resources aren't
leaked by wrapping the per-config processing (the call to rollup(config), the
outputs handling, and writeRollupOutput calls) in a try/finally so that
bundleResult.close() is always invoked; specifically, inside the loop where you
create const bundleResult = await rollup(config) (and before you inspect
outputs/ call writeRollupOutput), add a try { ... } finally { if (bundleResult
&& typeof bundleResult.close === "function") await bundleResult.close(); } to
guarantee close runs on both normal and error paths, and keep the existing outer
catch to log and return false.

"rootDir": ".",
"outDir": "../dist"
},
"references": [{ "path": "../" }],
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Find src/tsconfig.json files that reference their own parent directory.
# Expected result: Zero or only the files from this PR – indicating this is a systematic
# pattern to evaluate, not an isolated typo.

fd -g 'tsconfig.json' --full-path --search-path commands -E '*/node_modules/*' |
  xargs -I{} sh -c '
    dir=$(dirname {})
    parent=$(dirname "$dir")
    name=$(basename "$dir")
    if [ "$name" = "src" ]; then
      refs=$(python3 -c "
import json, sys
try:
    d = json.load(open(sys.argv[1]))
    print(json.dumps(d.get(\"references\", [])))
except: print(\"[]\")
" "{}")
      echo "=== {} ==="
      echo "  references: $refs"
    fi
  '

Repository: tsparticles/cli

Length of output: 41


🏁 Script executed:

# First, let's examine the file structure and actual tsconfig content
find commands/build-eslint -name 'tsconfig.json' -type f | sort

echo "=== Child tsconfig (src/tsconfig.json) ==="
cat commands/build-eslint/src/tsconfig.json

echo ""
echo "=== Parent tsconfig (commands/build-eslint/tsconfig.json) ==="
cat commands/build-eslint/tsconfig.json

Repository: tsparticles/cli

Length of output: 1615


references: [{ "path": "../" }] creates a backwards project-reference that causes double compilation.

src/tsconfig.json extends ../tsconfig.json (which already includes src/**/* and has composite: true), then also declares a project reference back to that same parent. When tsc -p src or tsc --build is invoked, TypeScript must build the parent first (compiling all src/**/* files to the parent's outDir: "."), then build src/tsconfig.json (compiling the exact same files again to ../dist). TypeScript's own documentation warns about this pattern: "have a 'solution' tsconfig.json file that simply has references to all of your leaf-node projects and sets files to an empty array (otherwise the solution file will cause double compilation of files)." The reference here is inverted – the leaf pointing at the solution/parent – producing the exact double-compilation scenario the docs caution against.

The extends is sufficient to inherit all compiler options from the parent. The references entry should be removed unless there is a deliberate intent to produce two separate build outputs for the same files.

🔧 Proposed fix
 {
   "extends": "../tsconfig.json",
   "compilerOptions": {
     "rootDir": ".",
     "outDir": "../dist"
   },
-  "references": [{ "path": "../" }],
   "include": ["**/*"]
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-eslint/src/tsconfig.json` at line 7, The "references": [{
"path": "../" }] entry in src/tsconfig.json creates a backwards
project-reference causing duplicate compilation; remove the references property
from src/tsconfig.json so it only relies on "extends": "../tsconfig.json" to
inherit compilerOptions and avoid double-building the same files, then run the
build (e.g., tsc -p src or tsc --build) to verify that files are no longer
compiled twice.

"@tsparticles/eslint-config": "^3.4.14",
"@tsparticles/prettier-config": "^3.4.14",
"@tsparticles/tsconfig": "^3.4.14",
"eslint": "^10.3.0",
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 | 🟠 Major | ⚡ Quick win

eslint belongs in devDependencies, not dependencies.

eslint is only invoked by the lint and lint:ci dev scripts. Placing it in dependencies ships ESLint as a mandatory transitive install for every downstream consumer of @tsparticles/cli-command-build-prettier, which is incorrect. Compare with commands/build-eslint/package.json, which correctly places eslint in peerDependencies/devDependencies only.

🐛 Proposed fix
   "dependencies": {
     "@swc/core": "^1.15.32",
-    "eslint": "^10.3.0",
     "eslint-config-prettier": "^10.1.8",
   "devDependencies": {
+    "eslint": "^10.3.0",
     "@types/estree": "^1.0.8",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-prettier/package.json` at line 42, Move the "eslint" entry out
of dependencies in package.json and add it to devDependencies instead: remove
the existing "eslint": "^10.3.0" line from the dependencies block and add the
same version under devDependencies so the lint and lint:ci dev scripts still
work but ESLint is not shipped as a runtime dependency of the package.

}
}

console.info("Build completed successfully!");
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 | 🟠 Major | ⚡ Quick win

console.info on completion bypasses options.silent

All other informational logging (Line 31) is guarded by options.silent, but the completion message on Line 42 always prints, leaking output in silent mode (e.g., during programmatic/pipe usage or CI capturing).

🔧 Proposed fix
-  console.info("Build completed successfully!");
+  if (!options.silent) {
+    console.info("Build completed successfully!");
+  }
📝 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
console.info("Build completed successfully!");
if (!options.silent) {
console.info("Build completed successfully!");
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build/src/nx-runner.ts` at line 42, The unconditional
console.info("Build completed successfully!") should be guarded by the existing
options.silent flag so it doesn't print in silent mode; update the console.info
call in nx-runner.ts to only log when options.silent is false (e.g., wrap it in
if (!options.silent) { ... } or use the same guarded logging path used for the
earlier info message), referencing the console.info call and the options.silent
variable to locate and change the code.

Comment thread nx.json
Comment on lines +34 to +36
"plugins": [
"@tsparticles/cli-nx-plugin"
],
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

🏁 Script executed:

#!/bin/bash
# Check if the plugin is declared in the root package.json
echo "=== Root package.json deps ==="
cat package.json | python3 -c "
import json, sys
pkg = json.load(sys.stdin)
deps = {**pkg.get('dependencies', {}), **pkg.get('devDependencies', {})}
key = '@tsparticles/cli-nx-plugin'
print(f'{key}: {deps.get(key, \"NOT FOUND\")}')
"

# Also verify packages/nx-plugin has a matching package name
echo "=== nx-plugin package name ==="
fd "package.json" packages/nx-plugin --max-depth 1 --exec python3 -c "
import json, sys
with open('{}') as f:
    pkg = json.load(f)
    print(pkg.get('name', 'N/A'))
"

Repository: tsparticles/cli

Length of output: 186


Add @tsparticles/cli-nx-plugin to root package.json devDependencies.

The plugin is referenced in nx.json but missing from the root package.json. Nx requires plugins to be resolvable from the workspace root's node_modules. Without adding "@tsparticles/cli-nx-plugin": "workspace:*" to root devDependencies, Nx will silently fail to load the plugin at workspace startup, and no canonical target aliases will be inferred.

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

In `@nx.json` around lines 34 - 36, Add the referenced plugin
"@tsparticles/cli-nx-plugin" to the root package.json devDependencies so Nx can
resolve it from the workspace root; open the root package.json and add an entry
like "@tsparticles/cli-nx-plugin": "workspace:*" under devDependencies, then run
npm/yarn install to ensure the plugin is present in node_modules and Nx can load
the plugin referenced in nx.json's "plugins" array.

Comment on lines +20 to +25
"build": "pnpm run prettify:src && pnpm run lint && pnpm run test && pnpm run prettify:readme",
"build:ci": "pnpm run prettify:ci:src && pnpm run lint:ci && pnpm run test:ci && pnpm run prettify:ci:readme",
"clear:dist": "rimraf ./dist",
"test": "vitest run",
"test:ci": "vitest run",
"prepack": "pnpm run build"
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 | ⚡ Quick win

Build script has no TypeScript compilation step and "main" points to raw source

The build (and build:ci) scripts run prettify, lint, and test, but never invoke tsc. Combined with "main": "src/index.ts", this means the plugin's TypeScript is never compiled to JavaScript, and the Nx workspace plugin loader hits the .js-import resolution failure seen in CI. Either:

  • Compile on build: add a tsc step to both scripts and change "main" to point at dist/index.js, or
  • Source-only: configure the Nx workspace's module loader (e.g., tsx) to handle .ts.js remapping when loading plugins.
🔧 Proposed fix (compile-on-build approach)
-  "build": "pnpm run prettify:src && pnpm run lint && pnpm run test && pnpm run prettify:readme",
-  "build:ci": "pnpm run prettify:ci:src && pnpm run lint:ci && pnpm run test:ci && pnpm run prettify:ci:readme",
+  "build": "pnpm run prettify:src && pnpm run lint && pnpm run tsc && pnpm run test && pnpm run prettify:readme",
+  "build:ci": "pnpm run prettify:ci:src && pnpm run lint:ci && pnpm run tsc && pnpm run test:ci && pnpm run prettify:ci:readme",
+  "tsc": "tsc -p src/tsconfig.json",

And update "main":

-  "main": "src/index.ts",
+  "main": "dist/index.js",
📝 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
"build": "pnpm run prettify:src && pnpm run lint && pnpm run test && pnpm run prettify:readme",
"build:ci": "pnpm run prettify:ci:src && pnpm run lint:ci && pnpm run test:ci && pnpm run prettify:ci:readme",
"clear:dist": "rimraf ./dist",
"test": "vitest run",
"test:ci": "vitest run",
"prepack": "pnpm run build"
"build": "pnpm run prettify:src && pnpm run lint && pnpm run tsc && pnpm run test && pnpm run prettify:readme",
"build:ci": "pnpm run prettify:ci:src && pnpm run lint:ci && pnpm run tsc && pnpm run test:ci && pnpm run prettify:ci:readme",
"tsc": "tsc -p src/tsconfig.json",
"clear:dist": "rimraf ./dist",
"test": "vitest run",
"test:ci": "vitest run",
"prepack": "pnpm run build"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nx-plugin/package.json` around lines 20 - 25, The build currently
runs prettify/lint/test but does not compile TypeScript and "main" points to
"src/index.ts"; update package.json so the build and build:ci scripts run tsc
(e.g., add a TypeScript compile step such as "pnpm run tsc:build" or directly
"tsc -p tsconfig.build.json") before prettify/ lint/tests (or place compilation
at the start), ensure the compiled output goes to dist, and change "main" from
"src/index.ts" to "dist/index.js"; also keep prepack invoking the build and
optionally run "clear:dist" before compiling to avoid stale artifacts
(referencing the build, build:ci, prepack, clear:dist and main entries).

Comment on lines +6 to +15
"build": {
"options": {
"main": "packages/nx-plugin/src/index.ts"
}
},
"build:ci": {
"options": {
"main": "packages/nx-plugin/src/index.ts"
}
}
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

🏁 Script executed:

#!/bin/bash
# Confirm no executor is inherited from nx.json targetDefaults for the "build" target name
rg -n '"build"' nx.json -A 5 | head -40

Repository: tsparticles/cli

Length of output: 187


🏁 Script executed:

#!/bin/bash
# Get full nx.json build target configuration
rg -n '"build"' nx.json -A 20 | head -80

Repository: tsparticles/cli

Length of output: 476


🏁 Script executed:

#!/bin/bash
# Get the packages/nx-plugin/project.json file contents
cat packages/nx-plugin/project.json

Repository: tsparticles/cli

Length of output: 437


Both build and build:ci targets are missing the executor field — TypeScript is never compiled

The main option alone is meaningless without an executor telling Nx how to use it. With no executor defined in either the project configuration or inherited from nx.json targetDefaults, Nx has no way to compile the TypeScript source to dist/, which is the root cause of the pipeline failure. The @nx/js:tsc executor (or equivalent) is required, along with outputPath and tsConfig.

🔧 Proposed fix
 "build": {
+  "executor": "@nx/js:tsc",
   "options": {
+    "outputPath": "packages/nx-plugin/dist",
+    "tsConfig": "packages/nx-plugin/src/tsconfig.json",
     "main": "packages/nx-plugin/src/index.ts"
   }
 },
 "build:ci": {
+  "executor": "@nx/js:tsc",
   "options": {
+    "outputPath": "packages/nx-plugin/dist",
+    "tsConfig": "packages/nx-plugin/src/tsconfig.json",
     "main": "packages/nx-plugin/src/index.ts"
   }
 }
📝 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
"build": {
"options": {
"main": "packages/nx-plugin/src/index.ts"
}
},
"build:ci": {
"options": {
"main": "packages/nx-plugin/src/index.ts"
}
}
"build": {
"executor": "@nx/js:tsc",
"options": {
"outputPath": "packages/nx-plugin/dist",
"tsConfig": "packages/nx-plugin/src/tsconfig.json",
"main": "packages/nx-plugin/src/index.ts"
}
},
"build:ci": {
"executor": "@nx/js:tsc",
"options": {
"outputPath": "packages/nx-plugin/dist",
"tsConfig": "packages/nx-plugin/src/tsconfig.json",
"main": "packages/nx-plugin/src/index.ts"
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/nx-plugin/project.json` around lines 6 - 15, The build and build:ci
targets only define "main" and lack an "executor", so TypeScript is never
compiled; update both targets ("build" and "build:ci") to use the `@nx/js`:tsc
executor (or another appropriate Nx executor) and add the required options
"outputPath" (e.g., dist/<project>) and "tsConfig" (path to your
tsconfig.app.json) alongside the existing "main" so Nx can produce compiled
output; ensure the same changes apply to both target blocks.

Comment thread packages/nx-plugin/src/index.ts Outdated
@@ -0,0 +1,9 @@
import { createNodesV2 } from "./create-nodes.js";
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 | ⚡ Quick win

Pipeline failure: create-nodes.js not resolvable from TypeScript source — fix is in project.json and package.json

The CI error is:

Nx failed to load plugin(s): @tsparticles/cli-nx-plugin. Cannot find module .../packages/nx-plugin/src/create-nodes.js

The .js extension in the import is correct TypeScript ESM convention, but Nx is loading src/index.ts directly (via "main": "src/index.ts" in package.json) without a TypeScript loader that remaps .js.ts. Since no compiled .js file exists (there is no tsc step anywhere), resolution fails. The fixes are in project.json and package.json — see comments on those files.

🧰 Tools
🪛 GitHub Actions: Node.js CI

[error] 1-1: Nx failed to load plugin(s): @tsparticles/cli-nx-plugin. Cannot find module '/home/runner/work/cli/cli/packages/nx-plugin/src/create-nodes.js' imported from /home/runner/work/cli/cli/packages/nx-plugin/src/index.ts.

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

In `@packages/nx-plugin/src/index.ts` at line 1, The import of createNodesV2 from
"./create-nodes.js" fails at runtime because Nx is loading the TypeScript source
(src/index.ts) directly and cannot resolve the .js extension to a .ts module;
update the package build/config so Nx loads the compiled output instead: change
the package.json "main" to point to a built JS entry (or remove pointing to
src), and update project.json targets (or outputs) to run a build step that
emits compiled .js files before Nx loads the plugin; ensure createNodesV2 still
imports the compiled create-nodes.js in the built package so runtime resolution
succeeds.

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: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@commands/build-tsc/src/tsc.ts`:
- Around line 30-34: The success message currently always prints via
console.info after calling buildTS; change it to respect the silent flag by only
printing when silent is falsy (e.g., wrap the console.info call in if (!silent)
or use an existing logger that honors silent). Update the post-build block in
tsc.ts (after buildTS(basePath, silent)) so the "TypeScript build finished
successfully!" message is emitted only when silent is not set.

In `@commands/build/src/build.ts`:
- Around line 89-115: The default "all" build path currently omits Rollup
because doBundleRollup is set to !!opts["bundleRollup"] (only true for an
explicit flag); change the Rollup behavior so Rollup runs when the overall "all"
plan is active by updating commandOptions to set doBundleRollup: all ||
!!opts["bundleRollup"] (leave the existing all calculation and other option
mappings as-is); reference variables: all, doBundleRollup, opts, commandOptions.
- Around line 87-104: The buildCommand.action handler currently ignores the
argPath and sets commandOptions.basePath to process.cwd(); change this to derive
basePath from the provided argPath (falling back to the documented default) e.g.
use path.resolve(process.cwd(), argPath || "src") so the BuildExecutionOptions
passed from buildCommand.action uses the intended target directory; update any
downstream uses (dist-stat collection) that rely on commandOptions.basePath to
ensure they operate on the resolved basePath.

In `@commands/build/src/legacy-runner.ts`:
- Around line 128-135: The success messages are printed unconditionally causing
noise when silent mode is requested; wrap the success log in the
aggregate-script branch and the per-step branch with a check for options.silent
(i.e., only call console.info("Build completed successfully!") when
!options.silent). Locate the aggregate branch that calls runScript(...) with
aggregateScript and the per-step loop/branch around lines handling individual
step scripts and apply the same !options.silent guard so both paths respect
silent mode.
🪄 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: 570c5ac6-dd58-4d2c-afcd-20d547d28d3a

📥 Commits

Reviewing files that changed from the base of the PR and between 6e5e3d1 and 578db7a.

📒 Files selected for processing (9)
  • commands/build-tsc/src/tsc.ts
  • commands/build/README.md
  • commands/build/src/build.ts
  • commands/build/src/legacy-runner.ts
  • commands/build/src/nx-runner.ts
  • packages/nx-plugin/src/create-nodes.ts
  • packages/nx-plugin/src/index.ts
  • packages/nx-plugin/src/tsconfig.json
  • packages/nx-plugin/tsconfig.json
✅ Files skipped from review due to trivial changes (1)
  • packages/nx-plugin/src/tsconfig.json

Comment on lines +30 to +34
if (!(await buildTS(basePath, silent))) {
throw new Error("TypeScript build failed");
}

console.log("Bundle finished successfully!");
console.info("TypeScript build finished successfully!");
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 | ⚡ Quick win

Honor silent for the success message.

console.info still emits even when --silent is set, so quiet invocations leak output at the end of a successful build.

♻️ Proposed fix
-  console.info("TypeScript build finished successfully!");
+  if (!silent) {
+    console.info("TypeScript build finished successfully!");
+  }
📝 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
if (!(await buildTS(basePath, silent))) {
throw new Error("TypeScript build failed");
}
console.log("Bundle finished successfully!");
console.info("TypeScript build finished successfully!");
if (!(await buildTS(basePath, silent))) {
throw new Error("TypeScript build failed");
}
if (!silent) {
console.info("TypeScript build finished successfully!");
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-tsc/src/tsc.ts` around lines 30 - 34, The success message
currently always prints via console.info after calling buildTS; change it to
respect the silent flag by only printing when silent is falsy (e.g., wrap the
console.info call in if (!silent) or use an existing logger that honors silent).
Update the post-build block in tsc.ts (after buildTS(basePath, silent)) so the
"TypeScript build finished successfully!" message is emitted only when silent is
not set.

Comment on lines 87 to +104
buildCommand.action(async (argPath: string) => {
const opts = buildCommand.opts(),
ci = !!opts["ci"],
all =
!!opts["all"] ||
(!opts["bundle"] &&
(!opts["bundleWebpack"] &&
!opts["bundleRollup"] &&
!opts["clean"] &&
!opts["circularDeps"] &&
!opts["dist"] &&
!opts["lint"] &&
!opts["prettify"] &&
!opts["tsc"]),
doBundle = all || !!opts["bundle"],
circularDeps = all || !!opts["circularDeps"],
clean = all || !!opts["clean"],
distfiles = all || !!opts["dist"],
doLint = all || !!opts["lint"],
prettier = all || !!opts["prettify"],
tsc = all || !!opts["tsc"],
silentOpt = opts["silent"] as string | boolean,
silent = silentOpt === "false" ? false : !!silentOpt || ci,
basePath = process.cwd(),
{ getDistStats } = await import("@tsparticles/cli-command-build-diststats"),
oldStats = await getDistStats(basePath);

if (clean) {
const { clearDist } = await import("@tsparticles/cli-command-build-clear");

await clearDist(basePath, silent);
}

const srcPath = path.join(basePath, argPath);

if (!existsSync(srcPath)) {
throw new Error("Provided path does not exist");
}

let canContinue = true;

if (prettier) {
const { prettifySrc } = await import("./build-prettier.js");

canContinue = await prettifySrc(basePath, srcPath, ci, silent);
}

if (canContinue && doLint) {
const { lint } = await import("./build-eslint.js");

canContinue = await lint(ci, silent);
}

if (canContinue && (tsc || circularDeps)) {
const checks: Promise<boolean>[] = [];

if (tsc) {
checks.push(import("./build-tsc.js").then(({ buildTS }) => buildTS(basePath, silent)));
commandOptions: BuildExecutionOptions = {
all,
argPath,
basePath: process.cwd(),
ci: !!opts["ci"],
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 | 🟠 Major | ⚡ Quick win

Use the path argument when deriving basePath.

build <path> currently ignores the argument and always targets process.cwd(), so both execution and dist-stat collection run against the wrong directory. The documented default of "src" never takes effect.

♻️ Proposed fix
+import path from "node:path";
...
-      basePath: process.cwd(),
+      basePath: path.resolve(process.cwd(), argPath),
📝 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
buildCommand.action(async (argPath: string) => {
const opts = buildCommand.opts(),
ci = !!opts["ci"],
all =
!!opts["all"] ||
(!opts["bundle"] &&
(!opts["bundleWebpack"] &&
!opts["bundleRollup"] &&
!opts["clean"] &&
!opts["circularDeps"] &&
!opts["dist"] &&
!opts["lint"] &&
!opts["prettify"] &&
!opts["tsc"]),
doBundle = all || !!opts["bundle"],
circularDeps = all || !!opts["circularDeps"],
clean = all || !!opts["clean"],
distfiles = all || !!opts["dist"],
doLint = all || !!opts["lint"],
prettier = all || !!opts["prettify"],
tsc = all || !!opts["tsc"],
silentOpt = opts["silent"] as string | boolean,
silent = silentOpt === "false" ? false : !!silentOpt || ci,
basePath = process.cwd(),
{ getDistStats } = await import("@tsparticles/cli-command-build-diststats"),
oldStats = await getDistStats(basePath);
if (clean) {
const { clearDist } = await import("@tsparticles/cli-command-build-clear");
await clearDist(basePath, silent);
}
const srcPath = path.join(basePath, argPath);
if (!existsSync(srcPath)) {
throw new Error("Provided path does not exist");
}
let canContinue = true;
if (prettier) {
const { prettifySrc } = await import("./build-prettier.js");
canContinue = await prettifySrc(basePath, srcPath, ci, silent);
}
if (canContinue && doLint) {
const { lint } = await import("./build-eslint.js");
canContinue = await lint(ci, silent);
}
if (canContinue && (tsc || circularDeps)) {
const checks: Promise<boolean>[] = [];
if (tsc) {
checks.push(import("./build-tsc.js").then(({ buildTS }) => buildTS(basePath, silent)));
commandOptions: BuildExecutionOptions = {
all,
argPath,
basePath: process.cwd(),
ci: !!opts["ci"],
import path from "node:path";
buildCommand.action(async (argPath: string) => {
const opts = buildCommand.opts(),
all =
!!opts["all"] ||
(!opts["bundleWebpack"] &&
!opts["bundleRollup"] &&
!opts["clean"] &&
!opts["circularDeps"] &&
!opts["dist"] &&
!opts["lint"] &&
!opts["prettify"] &&
!opts["tsc"]),
silentOpt = opts["silent"] as string | boolean,
commandOptions: BuildExecutionOptions = {
all,
argPath,
basePath: path.resolve(process.cwd(), argPath),
ci: !!opts["ci"],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build/src/build.ts` around lines 87 - 104, The buildCommand.action
handler currently ignores the argPath and sets commandOptions.basePath to
process.cwd(); change this to derive basePath from the provided argPath (falling
back to the documented default) e.g. use path.resolve(process.cwd(), argPath ||
"src") so the BuildExecutionOptions passed from buildCommand.action uses the
intended target directory; update any downstream uses (dist-stat collection)
that rely on commandOptions.basePath to ensure they operate on the resolved
basePath.

Comment on lines 89 to +115
all =
!!opts["all"] ||
(!opts["bundle"] &&
(!opts["bundleWebpack"] &&
!opts["bundleRollup"] &&
!opts["clean"] &&
!opts["circularDeps"] &&
!opts["dist"] &&
!opts["lint"] &&
!opts["prettify"] &&
!opts["tsc"]),
doBundle = all || !!opts["bundle"],
circularDeps = all || !!opts["circularDeps"],
clean = all || !!opts["clean"],
distfiles = all || !!opts["dist"],
doLint = all || !!opts["lint"],
prettier = all || !!opts["prettify"],
tsc = all || !!opts["tsc"],
silentOpt = opts["silent"] as string | boolean,
silent = silentOpt === "false" ? false : !!silentOpt || ci,
basePath = process.cwd(),
{ getDistStats } = await import("@tsparticles/cli-command-build-diststats"),
oldStats = await getDistStats(basePath);

if (clean) {
const { clearDist } = await import("@tsparticles/cli-command-build-clear");

await clearDist(basePath, silent);
}

const srcPath = path.join(basePath, argPath);

if (!existsSync(srcPath)) {
throw new Error("Provided path does not exist");
}

let canContinue = true;

if (prettier) {
const { prettifySrc } = await import("./build-prettier.js");

canContinue = await prettifySrc(basePath, srcPath, ci, silent);
}

if (canContinue && doLint) {
const { lint } = await import("./build-eslint.js");

canContinue = await lint(ci, silent);
}

if (canContinue && (tsc || circularDeps)) {
const checks: Promise<boolean>[] = [];

if (tsc) {
checks.push(import("./build-tsc.js").then(({ buildTS }) => buildTS(basePath, silent)));
commandOptions: BuildExecutionOptions = {
all,
argPath,
basePath: process.cwd(),
ci: !!opts["ci"],
circularDeps: all || !!opts["circularDeps"],
clean: all || !!opts["clean"],
distfiles: all || !!opts["dist"],
doBundleRollup: !!opts["bundleRollup"],
doBundleWebpack: all || !!opts["bundleWebpack"],
doLint: all || !!opts["lint"],
prettier: all || !!opts["prettify"],
silent: silentOpt === "false" ? false : !!silentOpt || !!opts["ci"],
tsc: all || !!opts["tsc"],
useNx: !!opts["nx"],
},
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 | 🟠 Major | ⚡ Quick win

Include rollup in the default all build plan.

doBundleRollup is only enabled when the explicit flag is passed, so the no-flag/default build path skips the rollup bundle step.

♻️ Proposed fix
-      doBundleRollup: !!opts["bundleRollup"],
+      doBundleRollup: all || !!opts["bundleRollup"],
📝 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
all =
!!opts["all"] ||
(!opts["bundle"] &&
(!opts["bundleWebpack"] &&
!opts["bundleRollup"] &&
!opts["clean"] &&
!opts["circularDeps"] &&
!opts["dist"] &&
!opts["lint"] &&
!opts["prettify"] &&
!opts["tsc"]),
doBundle = all || !!opts["bundle"],
circularDeps = all || !!opts["circularDeps"],
clean = all || !!opts["clean"],
distfiles = all || !!opts["dist"],
doLint = all || !!opts["lint"],
prettier = all || !!opts["prettify"],
tsc = all || !!opts["tsc"],
silentOpt = opts["silent"] as string | boolean,
silent = silentOpt === "false" ? false : !!silentOpt || ci,
basePath = process.cwd(),
{ getDistStats } = await import("@tsparticles/cli-command-build-diststats"),
oldStats = await getDistStats(basePath);
if (clean) {
const { clearDist } = await import("@tsparticles/cli-command-build-clear");
await clearDist(basePath, silent);
}
const srcPath = path.join(basePath, argPath);
if (!existsSync(srcPath)) {
throw new Error("Provided path does not exist");
}
let canContinue = true;
if (prettier) {
const { prettifySrc } = await import("./build-prettier.js");
canContinue = await prettifySrc(basePath, srcPath, ci, silent);
}
if (canContinue && doLint) {
const { lint } = await import("./build-eslint.js");
canContinue = await lint(ci, silent);
}
if (canContinue && (tsc || circularDeps)) {
const checks: Promise<boolean>[] = [];
if (tsc) {
checks.push(import("./build-tsc.js").then(({ buildTS }) => buildTS(basePath, silent)));
commandOptions: BuildExecutionOptions = {
all,
argPath,
basePath: process.cwd(),
ci: !!opts["ci"],
circularDeps: all || !!opts["circularDeps"],
clean: all || !!opts["clean"],
distfiles: all || !!opts["dist"],
doBundleRollup: !!opts["bundleRollup"],
doBundleWebpack: all || !!opts["bundleWebpack"],
doLint: all || !!opts["lint"],
prettier: all || !!opts["prettify"],
silent: silentOpt === "false" ? false : !!silentOpt || !!opts["ci"],
tsc: all || !!opts["tsc"],
useNx: !!opts["nx"],
},
all =
!!opts["all"] ||
(!opts["bundleWebpack"] &&
!opts["bundleRollup"] &&
!opts["clean"] &&
!opts["circularDeps"] &&
!opts["dist"] &&
!opts["lint"] &&
!opts["prettify"] &&
!opts["tsc"]),
silentOpt = opts["silent"] as string | boolean,
commandOptions: BuildExecutionOptions = {
all,
argPath,
basePath: process.cwd(),
ci: !!opts["ci"],
circularDeps: all || !!opts["circularDeps"],
clean: all || !!opts["clean"],
distfiles: all || !!opts["dist"],
doBundleRollup: all || !!opts["bundleRollup"],
doBundleWebpack: all || !!opts["bundleWebpack"],
doLint: all || !!opts["lint"],
prettier: all || !!opts["prettify"],
silent: silentOpt === "false" ? false : !!silentOpt || !!opts["ci"],
tsc: all || !!opts["tsc"],
useNx: !!opts["nx"],
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build/src/build.ts` around lines 89 - 115, The default "all" build
path currently omits Rollup because doBundleRollup is set to
!!opts["bundleRollup"] (only true for an explicit flag); change the Rollup
behavior so Rollup runs when the overall "all" plan is active by updating
commandOptions to set doBundleRollup: all || !!opts["bundleRollup"] (leave the
existing all calculation and other option mappings as-is); reference variables:
all, doBundleRollup, opts, commandOptions.

Comment on lines +128 to +135
if (aggregateScript) {
if (
!runScript(options.basePath, resolvePackageManager(packageJson.packageManager), aggregateScript, options.silent)
) {
throw new Error(`Legacy build script failed: ${aggregateScript}`);
}

console.info("Build completed successfully!");
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 | ⚡ Quick win

Guard both success logs when silent is enabled.

Both the aggregate-script path and the per-step path print Build completed successfully! unconditionally, so quiet builds still emit stdout.

🔧 Proposed fix
-      console.info("Build completed successfully!");
+      if (!options.silent) {
+        console.info("Build completed successfully!");
+      }
@@
-  console.info("Build completed successfully!");
+  if (!options.silent) {
+    console.info("Build completed successfully!");
+  }

Also applies to: 217-223

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

In `@commands/build/src/legacy-runner.ts` around lines 128 - 135, The success
messages are printed unconditionally causing noise when silent mode is
requested; wrap the success log in the aggregate-script branch and the per-step
branch with a check for options.silent (i.e., only call console.info("Build
completed successfully!") when !options.silent). Locate the aggregate branch
that calls runScript(...) with aggregateScript and the per-step loop/branch
around lines handling individual step scripts and apply the same !options.silent
guard so both paths respect silent mode.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@commands/build-bundle-webpack/package.json`:
- Around line 10-14: Update the package.json repository metadata so the
repository.directory field points to the renamed package folder; specifically
change the "repository.directory" value from "commands/build-bundle" to
"commands/build-bundle-webpack" so published source links resolve to the correct
subdirectory.

In `@commands/build-bundle-webpack/src/bundle-webpack.ts`:
- Around line 19-35: The final success message is always printed even when
--silent or CI mode is requested; update the bundleWebpackCommand.action handler
to check the computed silent boolean (derived from opts, ci, and silentOpt)
before calling console.info so the success log is skipped when silent is true;
locate the silent calculation and the console.info("Webpack bundling completed
successfully!") call in bundleWebpackCommand.action and wrap or gate that
console.info behind an if (!silent) check (or equivalent) so the message
respects --silent/CI.
🪄 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: b926246f-e755-4a11-97f6-51e842088078

📥 Commits

Reviewing files that changed from the base of the PR and between 578db7a and ad3e416.

📒 Files selected for processing (9)
  • commands/build-bundle-webpack/.dependency-cruiser.cjs
  • commands/build-bundle-webpack/README.md
  • commands/build-bundle-webpack/eslint.config.js
  • commands/build-bundle-webpack/package.json
  • commands/build-bundle-webpack/renovate.json
  • commands/build-bundle-webpack/src/bundle-webpack.ts
  • commands/build-bundle-webpack/src/tsconfig.json
  • commands/build-bundle-webpack/src/utils.ts
  • commands/build-bundle-webpack/tsconfig.json
✅ Files skipped from review due to trivial changes (1)
  • commands/build-bundle-webpack/src/tsconfig.json

Comment thread commands/build-bundle-webpack/package.json
Comment on lines +19 to +35
bundleWebpackCommand.action(async () => {
const opts = bundleWebpackCommand.opts(),
ci = !!opts["ci"],
silentOpt = opts["silent"] as string | boolean,
silent = silentOpt === "false" ? false : !!silentOpt || ci,
basePath = process.cwd();

if (!existsSync(basePath)) {
throw new Error("Provided path does not exist");
}

if (!(await bundleWebpack(basePath, silent))) {
throw new Error("Webpack bundling failed");
}

console.info("Webpack bundling completed successfully!");
});
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 | ⚡ Quick win

Respect --silent on the final success log.

This console.info still runs during --ci / --silent executions, so the command prints output even when the caller explicitly asked to suppress it.

♻️ Suggested fix
   if (!(await bundleWebpack(basePath, silent))) {
     throw new Error("Webpack bundling failed");
   }
 
-  console.info("Webpack bundling completed successfully!");
+  if (!silent) {
+    console.info("Webpack bundling completed successfully!");
+  }
 });
📝 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
bundleWebpackCommand.action(async () => {
const opts = bundleWebpackCommand.opts(),
ci = !!opts["ci"],
silentOpt = opts["silent"] as string | boolean,
silent = silentOpt === "false" ? false : !!silentOpt || ci,
basePath = process.cwd();
if (!existsSync(basePath)) {
throw new Error("Provided path does not exist");
}
if (!(await bundleWebpack(basePath, silent))) {
throw new Error("Webpack bundling failed");
}
console.info("Webpack bundling completed successfully!");
});
bundleWebpackCommand.action(async () => {
const opts = bundleWebpackCommand.opts(),
ci = !!opts["ci"],
silentOpt = opts["silent"] as string | boolean,
silent = silentOpt === "false" ? false : !!silentOpt || ci,
basePath = process.cwd();
if (!existsSync(basePath)) {
throw new Error("Provided path does not exist");
}
if (!(await bundleWebpack(basePath, silent))) {
throw new Error("Webpack bundling failed");
}
if (!silent) {
console.info("Webpack bundling completed successfully!");
}
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/build-bundle-webpack/src/bundle-webpack.ts` around lines 19 - 35,
The final success message is always printed even when --silent or CI mode is
requested; update the bundleWebpackCommand.action handler to check the computed
silent boolean (derived from opts, ci, and silentOpt) before calling
console.info so the success log is skipped when silent is true; locate the
silent calculation and the console.info("Webpack bundling completed
successfully!") call in bundleWebpackCommand.action and wrap or gate that
console.info behind an if (!silent) check (or equivalent) so the message
respects --silent/CI.

Copy link
Copy Markdown

@nx-cloud nx-cloud Bot left a comment

Choose a reason for hiding this comment

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

Nx Cloud is proposing a fix for your failed CI:

We added a paths entry to commands/build-bundle-rollup/tsconfig.json to fix the ESLint no-unsafe-call and no-unsafe-return errors caused by TypeScript being unable to resolve @tsparticles/rollup-plugin types (its dist/ hasn't been built yet). By mapping the package directly to its workspace source at ../../utils/rollup-plugin/src/index.ts, TypeScript can now correctly type loadParticlesBundle as (p: ParticlesBuildParams) => RollupOptions[], satisfying both unsafe-call and unsafe-return rules without touching business logic.

Warning

We could not verify this fix.

diff --git a/commands/build-bundle-rollup/tsconfig.json b/commands/build-bundle-rollup/tsconfig.json
index e0ed61e..d50d1be 100644
--- a/commands/build-bundle-rollup/tsconfig.json
+++ b/commands/build-bundle-rollup/tsconfig.json
@@ -2,6 +2,9 @@
   "compilerOptions": {
     "rootDir": ".",
     "outDir": ".",
+    "paths": {
+      "@tsparticles/rollup-plugin": ["../../utils/rollup-plugin/src/index.ts"]
+    },
     "resolveJsonModule": true,
     "composite": true,
     "target": "ESNext",

Apply fix via Nx Cloud  Reject fix via Nx Cloud


Or Apply changes locally with:

npx nx-cloud apply-locally rb16-uUdz

Apply fix locally with your editor ↗   View interactive diff ↗



🎓 Learn more about Self-Healing CI on nx.dev

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants