Skip to content

3.1.1#89

Merged
matteobruni merged 22 commits intomainfrom
dev
Feb 2, 2026
Merged

3.1.1#89
matteobruni merged 22 commits intomainfrom
dev

Conversation

@matteobruni
Copy link
Contributor

@matteobruni matteobruni commented Feb 2, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced centralized dependency graph policy with comprehensive ruleset for detecting circular dependencies, orphans, and other dependency issues.
  • Chores

    • Version bumped to 3.1.1.
    • Updated development dependencies: ESLint, Prettier, TypeScript, and tsparticles packages.
    • Replaced circular dependency detection tool for improved analysis.
    • Added swc-loader to build tooling.
    • Applied code formatting standardization across the project.

Copilot AI review requested due to automatic review settings February 2, 2026 07:43
@coderabbitai
Copy link

coderabbitai bot commented Feb 2, 2026

Warning

Rate limit exceeded

@matteobruni has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 47 minutes and 24 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

This PR introduces Dependency Cruiser as a replacement for Madge for circular dependency detection, updates Node.js module specifiers to use the node: prefix across multiple source files, bumps numerous package dependencies (including TypeScript, ESLint plugins, and webpack-related tools), and normalizes code formatting with consistent indentation throughout the codebase.

Changes

Cohort / File(s) Summary
Dependency Cruiser Configuration
.dependency-cruiser.cjs
New configuration file exporting an IConfiguration object with forbidden rules (no-circular, no-orphans, no-deprecated-core, etc.), severity levels, and comprehensive options for module resolution, scanning behavior, TypeScript integration, and reporter customization (+382 lines).
Build System - Circular Dependency Detection
src/build/build-circular-deps.ts
Replaces Madge-based logic with Dependency Cruiser; reads optional .dependency-cruiser.js/cjs config, builds cruise options with TypeScript config, runs analysis on src directory, filters for no-circular violations, and logs progress/results. Enhanced error handling and user-facing logging added.
Build System - Formatting & Node Import Updates
src/build/build-bundle.ts, src/build/build-clear.ts, src/build/build-distfiles.ts, src/build/build-diststats.ts, src/build/build-eslint.ts, src/build/build-prettier.ts, src/build/build-tsc.ts, src/build/build.ts
Updates Node.js module specifiers to node:path; formatting and indentation normalization; Prettier config adjustments (tabWidth 4→2, printWidth 120, endOfLine "lf"); refined error handling structure and control flow cosmetics; no functional behavior changes.
Create Commands - Node Import & Formatting
src/create/plugin/create-plugin.ts, src/create/plugin/plugin.ts, src/create/preset/create-preset.ts, src/create/preset/preset.ts, src/create/shape/create-shape.ts, src/create/shape/shape.ts
Replaces "path" import with "node:path"; reformats variable declarations, object literals, and indentation; consolidates multi-line blocks; no functional logic changes.
Utilities & CLI - Node Import Updates
src/cli.ts, src/utils/file-utils.ts, src/utils/string-utils.ts, src/utils/template-utils.ts
Updates built-in module specifiers (child_process, path → node:child_process, node:path); applies formatting/indentation normalization; no behavioral changes.
Tests - Node Import Updates
tests/create-plugin.test.ts, tests/create-preset.test.ts, tests/create-shape.test.ts, tests/file-utils.test.ts
Replaces "path" import with "node:path"; no other test logic changes.
Package Configuration
package.json (root), files/empty-project/package.json
Root: Version bump 3.0.14→3.1.1; replaces madge with dependency-cruiser; adds swc-loader; updates @tsparticles configs, TypeScript, ESLint plugins, Prettier, webpack-bundle-analyzer, vitest; updates CLI to 3.1.0. Empty project: Updates devDependencies versions (ESLint config, Prettier config, TS config, Webpack plugin, Prettier, TypeScript ESLint); removes babel-loader.
Workspace & TypeScript Configuration
pnpm-workspace.yaml, tsconfig.json (root), src/tsconfig.json
Workspace: Replaces onlyBuiltDependencies '@tsparticles/engine' and 'esbuild' with '@swc/core'. Root tsconfig.json: Removes "madge" from compilerOptions.types. All: Formatting/indentation normalization.
Browserlist Configuration
files/empty-project/.browserslistrc
Updates "since" directive year from 2019 to 2021; no other changes.

Sequence Diagram

sequenceDiagram
    participant BuildSystem as Build System
    participant DepCruiser as Dependency Cruiser
    participant FileSystem as File System
    participant Logger as Logger

    BuildSystem->>FileSystem: Read .dependency-cruiser.js/cjs
    FileSystem-->>BuildSystem: Config (or none)
    BuildSystem->>BuildSystem: Build cruise options<br/>(merge config + defaults)
    BuildSystem->>DepCruiser: cruise(src, cruiseOptions)
    DepCruiser->>DepCruiser: Analyze dependencies<br/>& detect violations
    DepCruiser-->>BuildSystem: Violation results
    BuildSystem->>BuildSystem: Filter for no-circular
    alt No circular deps found
        BuildSystem->>Logger: Log success
        BuildSystem-->>BuildSystem: Return true
    else Circular deps found
        BuildSystem->>Logger: Log cycle paths
        BuildSystem-->>BuildSystem: Return false
    end
    Logger-->>BuildSystem: Output complete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • 3.0.0 #62: Directly related; both PRs replace madge with dependency-cruiser for circular dependency detection and update the corresponding build-circular-deps implementation and package.json entries.

Suggested labels

size/XL

Poem

🐰 A rabbit hops through bundled code,

With node: prefixes on the road,

Cruiser replaces madge's old way,

While prettier makes formatting play.

Deps aligned, the build runs true,

Version bumped to 3.1.2 (almost!) anew! 🚀

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title '3.1.1' is a version number that does not clearly describe the main changes made in this pull request, which include migration from madge to dependency-cruiser, multiple dependency updates, and various code formatting changes. Use a descriptive title that summarizes the primary changes, such as 'Migrate from madge to dependency-cruiser and update dependencies' or 'v3.1.1: Replace circular deps tool and update tooling'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

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 updates the package version from 3.0.14 to 3.1.1 and includes several modernization improvements to the build toolchain and code formatting.

Changes:

  • Replaced madge with dependency-cruiser for circular dependency detection
  • Migrated from Babel to SWC for faster builds (added @swc/core and swc-loader, removed babel-loader and @babel/core)
  • Standardized Node.js module imports to use node: protocol (e.g., node:path, node:child_process)
  • Reformatted code from 4-space to 2-space indentation throughout the codebase
  • Updated various dependency versions and adjusted browserslist target from 2019 to 2021

Reviewed changes

Copilot reviewed 25 out of 31 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
package.json Updated version to 3.1.1, replaced madge with dependency-cruiser, added SWC support, removed Babel dependencies, updated circular-deps script
tsconfig.json Removed "madge" from types array as it's no longer a dependency
src/build/build-circular-deps.ts Complete rewrite to use dependency-cruiser instead of madge with improved error reporting and configuration support
src/build/build-prettier.ts Changed tabWidth from 4 to 2 and reformatted all code with 2-space indentation
src/**/*.ts Standardized imports to use node: protocol for built-in modules and reformatted with 2-space indentation
tests/**/*.test.ts Updated path imports to use node:path and reformatted with 2-space indentation
.dependency-cruiser.cjs Added comprehensive dependency-cruiser configuration file with various rule definitions
pnpm-workspace.yaml Added @swc/core to onlyBuiltDependencies for build optimization
files/empty-project/.browserslistrc Updated browser support target from 2019 to 2021
files/empty-project/package.json Updated CLI and dependency versions, removed Babel dependencies

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

@matteobruni matteobruni merged commit 6eb7f49 into main Feb 2, 2026
2 checks passed
Copy link

@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: 6

🤖 Fix all issues with AI agents
In `@package.json`:
- Line 47: The package.json currently lists "dependency-cruiser" in dependencies
but it is a build-time tool used only by the "circular-deps" script; move the
"dependency-cruiser" entry from the top-level "dependencies" section into the
"devDependencies" section so it is treated as a dev-only dependency and not
installed for runtime consumers; update package.json by removing the
"dependency-cruiser" line from dependencies and adding the same entry (same
version string "^17.3.7") under "devDependencies".

In `@src/build/build-circular-deps.ts`:
- Around line 64-77: The default circular-check rule in the else branch that
sets cruiseOptions.ruleSet currently uses severity: "error" but the
.dependency-cruiser.cjs uses severity: "warn"; update the fallback in
build-circular-deps.ts (the else block that assigns cruiseOptions.ruleSet / the
"no-circular" rule) to use severity: "warn" (or otherwise match the config's
severity) so behavior is consistent whether or not a config file exists.

In `@src/build/build-prettier.ts`:
- Around line 115-125: Re-enable the CI formatting check by removing the
surrounding TODO/commented-out block and restoring the if (ci) branch: use
prettier.check(contents, options) when the ci flag is true and throw an Error if
it returns false (e.g., `package.dist.json is not formatted correctly`);
otherwise format via prettier.format(contents, options) and write the result
with fs.writeFile("package.dist.json", formatted, "utf8"). Make sure to keep the
existing variables/identifiers (ci, contents, options, prettier.check,
prettier.format, formatted, fs.writeFile) so CI will enforce formatting while
local runs still overwrite the file.

In `@src/create/plugin/create-plugin.ts`:
- Around line 96-139: The repoPath extraction in updateReadmeFile incorrectly
assumes repoUrl contains ".git"; update the logic in updateReadmeFile to
normalize repoUrl by stripping the protocol/host prefix (e.g., remove
"https://github.com/" or "http://github.com/" and optional "www.") and then
remove an optional trailing ".git" suffix before using it as repoPath; replace
the current substring/indexOf(".git") logic (references: repoUrl, stringSearch,
trailingSlashSearch, repoPath) with this normalized value and keep the existing
fallback ("tsparticles/plugin-template") if normalization yields an empty
string.

In `@src/create/preset/create-preset.ts`:
- Around line 148-151: The replacement string for the RegExp in create-preset.ts
is missing the closing brace/quote/backtick; update the `to` value so the
template interpolation is closed and the string is valid — e.g. change `to:
\`preset: "${camelizedName}\`` to `to: \`preset: "${camelizedName}"\`` (ensure
`${camelizedName}` is closed and the surrounding backtick/string quote is
present).

In `@src/create/shape/create-shape.ts`:
- Around line 135-138: The replacement string in the mapping object for
shape.type is missing the closing brace/quote around the template variable;
update the `to` value used alongside the `from: /shape\.type: "template"/g`
entry so the template variable `camelizedName` is properly closed (i.e., ensure
`${camelizedName}` is fully terminated inside the string literal) in the mapping
near create-shape.ts so the replacement produces a valid string `shape.type:
"..."`.
🧹 Nitpick comments (8)
src/cli.ts (1)

4-6: Inconsistent module specifier for url.

Line 4 imports fileURLToPath from "url" without the node: prefix, while line 6 uses "node:path". For consistency with the PR's goal of standardizing on node: prefixed imports for built-ins, consider updating the url import as well.

Suggested fix
-import { fileURLToPath } from "url";
+import { fileURLToPath } from "node:url";
src/build/build-bundle.ts (1)

38-48: Pre-existing: Missing return after reject.

Note that after reject(new Error(...)) on line 41, there's no return statement, so resolve(true) on line 48 will also be called. While JavaScript Promises ignore subsequent settlements, this pattern can cause confusion. This is a pre-existing issue, not introduced by this PR's formatting changes.

Optional fix
         if (stats.hasErrors()) {
           console.error(info.errors);

           reject(new Error(info.errors?.map(error => error.message).join("\n")));
+
+          return;
         }
src/utils/file-utils.ts (1)

20-33: Preserve RegExp flags and validate pattern sources.

new RegExp(token.from, "g") drops any non‑g flags from RegExp inputs and treats string inputs as raw regex patterns. If callers depend on flags (e.g., case‑insensitive) or if strings can include metacharacters, behavior changes and safety risks increase. Please confirm token.from is always trusted/static; otherwise consider preserving flags and escaping string inputs.

💡 Suggested safer construction
+const escapeRegExp = (value: string): string => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+
 export async function replaceTokensInFiles(options: ReplaceTokensOptions[]): Promise<void> {
   for (const item of options) {
     const filePath = item.path;

     let data = await fs.readFile(filePath, "utf-8");

     for (const token of item.tokens) {
-      const regex = new RegExp(token.from, "g");
+      const regex =
+        token.from instanceof RegExp
+          ? new RegExp(
+              token.from.source,
+              token.from.flags.includes("g") ? token.from.flags : `${token.from.flags}g`,
+            )
+          : new RegExp(escapeRegExp(token.from), "g");

       data = data.replace(regex, token.to);
     }

     await fs.writeFile(filePath, data);
   }
 }
src/build/build-circular-deps.ts (2)

31-77: Consider refactoring duplicated config loading logic.

The config loading for .dependency-cruiser.js (lines 32-47) and .dependency-cruiser.cjs (lines 48-63) are nearly identical. This could be extracted into a helper function.

♻️ Proposed refactor
+async function loadConfig(configPath: string, cruiseOptions: ICruiseOptions): Promise<ICruiseOptions> {
+  const configModule = (await import(configPath)) as {
+      default: {
+        forbidden?: IForbiddenRuleType[];
+        options?: ICruiseOptions;
+      };
+    },
+    extendedConfig = configModule.default;
+
+  return {
+    ...cruiseOptions,
+    ...extendedConfig.options,
+    ruleSet: {
+      forbidden: extendedConfig.forbidden ?? [],
+    },
+  };
+}

 // Then in buildCircularDeps:
   try {
     if (await fs.pathExists(configPath)) {
-      const configModule = (await import(configPath)) as {
-          default: {
-            forbidden?: IForbiddenRuleType[];
-            options?: ICruiseOptions;
-          };
-        },
-        extendedConfig = configModule.default;
-
-      cruiseOptions = {
-        ...cruiseOptions,
-        ...extendedConfig.options,
-        ruleSet: {
-          forbidden: extendedConfig.forbidden ?? [],
-        },
-      };
+      cruiseOptions = await loadConfig(configPath, cruiseOptions);
     } else if (await fs.pathExists(configPathCjs)) {
-      const configModule = (await import(configPathCjs)) as {
-          default: {
-            forbidden?: IForbiddenRuleType[];
-            options?: ICruiseOptions;
-          };
-        },
-        extendedConfig = configModule.default;
-
-      cruiseOptions = {
-        ...cruiseOptions,
-        ...extendedConfig.options,
-        ruleSet: {
-          forbidden: extendedConfig.forbidden ?? [],
-        },
-      };
+      cruiseOptions = await loadConfig(configPathCjs, cruiseOptions);
     } else {

87-93: Remove unnecessary type assertion on cycle step.

Since violation.cycle is already typed as IMiniDependency[] from dependency-cruiser (which has a name property), the inline type assertion is unnecessary.

♻️ Proposed improvement
       for (const violation of circularViolations) {
-        const cyclePath = (violation.cycle ?? []).map(step => {
-          return (step as { name: string }).name;
-        });
+        const cyclePath = (violation.cycle ?? []).map(step => step.name);

         console.error(`Cycle detected: ${cyclePath.join(" -> ")}`);
       }
src/build/build-distfiles.ts (1)

29-33: Clarify the dependency copying logic.

The else if means only one of dependencies or peerDependencies gets copied to the dist package. If a package has both, peerDependencies will be ignored. If this is intentional, a comment would help clarify the design decision.

📝 Suggested comment
+    // Copy dependencies first; if none exist, fall back to peerDependencies
+    // Note: If both exist, only dependencies is used (peerDependencies are typically
+    // expected to be provided by the consuming project)
     if (pkgInfo.dependencies) {
       libObj["dependencies"] = JSON.parse(JSON.stringify(pkgInfo.dependencies).replaceAll("workspace:", ""));
     } else if (pkgInfo.peerDependencies) {
       libObj["peerDependencies"] = JSON.parse(JSON.stringify(pkgInfo.peerDependencies).replaceAll("workspace:", ""));
     }
src/build/build-prettier.ts (1)

193-222: Simplify the early return flow.

The pattern of setting res = true when folder doesn't exist, then checking if (!res) is a bit convoluted. Consider using early return for clarity.

♻️ Suggested simplification
   try {
     const folder = "traduction",
       folderPath = path.join(basePath, folder);

-    if (!fs.existsSync(folderPath)) {
-      res = true;
-    }
-
-    if (!res) {
+    if (!fs.existsSync(folderPath)) {
+      console.log("Prettier - done on traductions (folder not found)");
+      return true;
+    }
+
     for await (const file of klaw(folderPath)) {
       // ... loop body
     }

     res = true;
-    }
   } catch (e) {
src/build/build.ts (1)

49-51: Inconsistent path module import.

Line 49 uses await import("path") while other files in this PR have migrated to node:path. For consistency, consider updating this dynamic import as well.

♻️ Proposed fix
-  const path = await import("path"),
+  const path = await import("node:path"),
     srcPath = path.join(basePath, argPath),
     fs = await import("fs-extra");

"klaw": "^4.1.0",
"lookpath": "^1.2.3",
"madge": "^8.0.0",
"dependency-cruiser": "^17.3.7",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Move dependency-cruiser to devDependencies.

dependency-cruiser is used only in the circular-deps script for validating the CLI's own codebase during development/build. It's not a runtime dependency needed by CLI users. It should be in devDependencies like other build-time tools.

Proposed fix

Move line 47 to the devDependencies section:

  "dependencies": {
    ...
-   "dependency-cruiser": "^17.3.7",
    ...
  },
  "devDependencies": {
+   "dependency-cruiser": "^17.3.7",
    ...
  }
🤖 Prompt for AI Agents
In `@package.json` at line 47, The package.json currently lists
"dependency-cruiser" in dependencies but it is a build-time tool used only by
the "circular-deps" script; move the "dependency-cruiser" entry from the
top-level "dependencies" section into the "devDependencies" section so it is
treated as a dev-only dependency and not installed for runtime consumers; update
package.json by removing the "dependency-cruiser" line from dependencies and
adding the same entry (same version string "^17.3.7") under "devDependencies".

Comment on lines +64 to 77
} else {
console.log("No .dependency-cruiser.js found, applying default circular check.");

cruiseOptions.ruleSet = {
forbidden: [
{
name: "no-circular",
severity: "error",
from: {},
to: { circular: true },
},
],
};
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Severity inconsistency between default and config file.

The default fallback uses severity: "error" (line 71), but the .dependency-cruiser.cjs config file uses severity: "warn". This could lead to different behavior depending on whether a config file exists. Consider aligning these for consistent behavior.

🤖 Prompt for AI Agents
In `@src/build/build-circular-deps.ts` around lines 64 - 77, The default
circular-check rule in the else branch that sets cruiseOptions.ruleSet currently
uses severity: "error" but the .dependency-cruiser.cjs uses severity: "warn";
update the fallback in build-circular-deps.ts (the else block that assigns
cruiseOptions.ruleSet / the "no-circular" rule) to use severity: "warn" (or
otherwise match the config's severity) so behavior is consistent whether or not
a config file exists.

Comment on lines +115 to +125
// TODO: disabled this check until "prettier-plugin-multiline-arrays" package is compatible with Prettier 3.0.0

/* if (ci) {
/* if (ci) {
if (!(await prettier.check(contents, options))) {
throw new Error(`package.dist.json is not formatted correctly`);
}
} else { */
const formatted = await prettier.format(contents, options);
const formatted = await prettier.format(contents, options);

await fs.writeFile("package.dist.json", formatted, "utf8");
// }
await fs.writeFile("package.dist.json", formatted, "utf8");
// }
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

prettier-plugin-multiline-arrays Prettier 3 support compatibility

💡 Result:

prettier-plugin-multiline-arrays does support Prettier 3, as long as you use a recent version of the plugin:

  • Latest (v4.1.3) declares peerDependencies of prettier: ">=3.0.0 <4.0.0", so it’s intended to work with all Prettier 3.x releases. It’s also ESM-only ("type": "module") and requires Node >= 20. [1]
  • This aligns with Prettier 3’s plugin API changes (including ESM plugin support), which is why older Prettier-2-era plugins often needed updates. [2]

If you’re on Prettier 3 and hit install warnings/errors, ensure you’re on plugin v4.x (preferably latest) and running Node 20+. [1]

Sources: [1], [2]


Re-enable the CI format check—the plugin now supports Prettier 3.

The prettier-plugin-multiline-arrays package (v4.1.3+) now explicitly supports Prettier 3 with peerDependencies of prettier: ">=3.0.0 <4.0.0". The TODO blocking the CI check is outdated. Remove the comment and restore the check to ensure package.dist.json is properly formatted on CI.

🤖 Prompt for AI Agents
In `@src/build/build-prettier.ts` around lines 115 - 125, Re-enable the CI
formatting check by removing the surrounding TODO/commented-out block and
restoring the if (ci) branch: use prettier.check(contents, options) when the ci
flag is true and throw an Error if it returns false (e.g., `package.dist.json is
not formatted correctly`); otherwise format via prettier.format(contents,
options) and write the result with fs.writeFile("package.dist.json", formatted,
"utf8"). Make sure to keep the existing variables/identifiers (ci, contents,
options, prettier.check, prettier.format, formatted, fs.writeFile) so CI will
enforce formatting while local runs still overwrite the file.

Comment on lines 96 to +139
async function updateReadmeFile(destPath: string, name: string, description: string, repoUrl: string): Promise<void> {
const readmePath = path.join(destPath, "README.md"),
capitalizedName = capitalize(name, "-", " "),
camelizedName = camelize(capitalizedName),
dashedName = dash(camelizedName),
stringSearch = "github.com",
trailingSlashSearch = "github.com/",
repoPath = repoUrl.includes(stringSearch)
? repoUrl.substring(
repoUrl.indexOf(trailingSlashSearch) + trailingSlashSearch.length,
repoUrl.indexOf(".git"),
)
: "tsparticles/plugin-template";
const readmePath = path.join(destPath, "README.md"),
capitalizedName = capitalize(name, "-", " "),
camelizedName = camelize(capitalizedName),
dashedName = dash(camelizedName),
stringSearch = "github.com",
trailingSlashSearch = "github.com/",
repoPath = repoUrl.includes(stringSearch)
? repoUrl.substring(repoUrl.indexOf(trailingSlashSearch) + trailingSlashSearch.length, repoUrl.indexOf(".git"))
: "tsparticles/plugin-template";

await replaceTokensInFile({
path: readmePath,
tokens: [
{
from: /tsParticles Template Plugin/g,
to: `tsParticles ${description} Plugin`,
},
{
from: /tsparticles-plugin-template/g,
to: `tsparticles-plugin-${dashedName}`,
},
{
from: /tsparticles\.plugin\.template(\.bundle)?\.min\.js/g,
to: `tsparticles.plugin.${camelizedName}$1.min.js`,
},
{
from: /loadTemplatePlugin/g,
to: `load${capitalizedName}Plugin`,
},
{
from: /\[tsParticles]\(https:\/\/github.com\/matteobruni\/tsparticles\) additional template plugin\./g,
to: `[tsParticles](https://github.com/matteobruni/tsparticles) additional ${name} plugin.`,
},
{
from: /plugin\.type: "template"/g,
to: `plugin.type: "${camelizedName}"`,
},
{
from: /!\[demo]\(https:\/\/raw.githubusercontent.com\/tsparticles\/plugin-template\/main\/images\/sample.png\)/g,
to: `![demo](https://raw.githubusercontent.com/${repoPath}/main/images/sample.png)`,
},
],
});
await replaceTokensInFile({
path: readmePath,
tokens: [
{
from: /tsParticles Template Plugin/g,
to: `tsParticles ${description} Plugin`,
},
{
from: /tsparticles-plugin-template/g,
to: `tsparticles-plugin-${dashedName}`,
},
{
from: /tsparticles\.plugin\.template(\.bundle)?\.min\.js/g,
to: `tsparticles.plugin.${camelizedName}$1.min.js`,
},
{
from: /loadTemplatePlugin/g,
to: `load${capitalizedName}Plugin`,
},
{
from: /\[tsParticles]\(https:\/\/github.com\/matteobruni\/tsparticles\) additional template plugin\./g,
to: `[tsParticles](https://github.com/matteobruni/tsparticles) additional ${name} plugin.`,
},
{
from: /plugin\.type: "template"/g,
to: `plugin.type: "${camelizedName}"`,
},
{
from: /!\[demo]\(https:\/\/raw.githubusercontent.com\/tsparticles\/plugin-template\/main\/images\/sample.png\)/g,
to: `![demo](https://raw.githubusercontent.com/${repoPath}/main/images/sample.png)`,
},
],
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the file to understand the full context
cat -n src/create/plugin/create-plugin.ts | sed -n '90,145p'

Repository: tsparticles/cli

Length of output: 2524


🏁 Script executed:

# Search for any tests related to updateReadmeFile
fd -type f -name "*.test.ts" -o -name "*.spec.ts" | xargs rg -l "updateReadmeFile|create-plugin" 2>/dev/null || echo "No test files found"

Repository: tsparticles/cli

Length of output: 405


🏁 Script executed:

# Let's verify JavaScript substring behavior with -1
node << 'EOF'
// Test substring behavior with indexOf returning -1
const url = "https://github.com/user/repo";
const withGit = "https://github.com/user/repo.git";

const trailingSlashSearch = "github.com/";
const startIdx = url.indexOf(trailingSlashSearch) + trailingSlashSearch.length;
const endIdx = url.indexOf(".git");

console.log("URL without .git:", url);
console.log("startIdx:", startIdx);
console.log("endIdx:", endIdx);
console.log("substring result:", url.substring(startIdx, endIdx));
console.log("substring result is empty:", url.substring(startIdx, endIdx) === "");

console.log("\nURL with .git:", withGit);
console.log("startIdx:", withGit.indexOf(trailingSlashSearch) + trailingSlashSearch.length);
console.log("endIdx:", withGit.indexOf(".git"));
console.log("substring result:", withGit.substring(withGit.indexOf(trailingSlashSearch) + trailingSlashSearch.length, withGit.indexOf(".git")));
EOF

Repository: tsparticles/cli

Length of output: 301


🏁 Script executed:

# Check if there are any actual calls to this function to understand if URLs without .git are realistic
rg "updateReadmeFile|repoUrl" src/create/plugin/create-plugin.ts -B 3 -A 3

Repository: tsparticles/cli

Length of output: 2851


🏁 Script executed:

# Check the test file for coverage of updateReadmeFile and URL handling
cat -n tests/create-plugin.test.ts | head -100

Repository: tsparticles/cli

Length of output: 1603


🏁 Script executed:

# Search for test cases that might test URLs with and without .git
rg "updateReadmeFile|repoUrl|\.git" tests/create-plugin.test.ts -B 2 -A 2

Repository: tsparticles/cli

Length of output: 41


🏁 Script executed:

# Verify the proposed fix handles various URL formats correctly
node << 'EOF'
const testUrls = [
  "https://github.com/user/repo",
  "https://github.com/user/repo.git",
  "git@github.com:user/repo",
  "git@github.com:user/repo.git",
  "https://github.com/matteobruni/tsparticles",
];

console.log("Testing proposed fix:");
testUrls.forEach(url => {
  const repoPath = url
    .replace(/^git@github\.com:/, "")
    .replace(/^https?:\/\/github\.com\//, "")
    .replace(/\.git$/, "");
  console.log(`Input: ${url}`);
  console.log(`Output: ${repoPath}\n`);
});
EOF

Repository: tsparticles/cli

Length of output: 383


Handle repository URLs that omit the .git suffix.

The test at tests/create-plugin.test.ts:28 passes "https://github.com/matteobruni/tsparticles" without a .git suffix. When indexOf(".git") returns -1, the current substring() call produces an incorrect repoPath ("https://github.com/" instead of "matteobruni/tsparticles"), breaking the README demo image link. Normalize the URL by stripping the protocol/host and removing an optional .git suffix:

-    repoPath = repoUrl.includes(stringSearch)
-      ? repoUrl.substring(repoUrl.indexOf(trailingSlashSearch) + trailingSlashSearch.length, repoUrl.indexOf(".git"))
-      : "tsparticles/plugin-template";
+    repoPath = repoUrl.includes(stringSearch)
+      ? repoUrl
+          .replace(/^git@github\.com:/, "")
+          .replace(/^https?:\/\/github\.com\//, "")
+          .replace(/\.git$/, "")
+      : "tsparticles/plugin-template";
🤖 Prompt for AI Agents
In `@src/create/plugin/create-plugin.ts` around lines 96 - 139, The repoPath
extraction in updateReadmeFile incorrectly assumes repoUrl contains ".git";
update the logic in updateReadmeFile to normalize repoUrl by stripping the
protocol/host prefix (e.g., remove "https://github.com/" or "http://github.com/"
and optional "www.") and then remove an optional trailing ".git" suffix before
using it as repoPath; replace the current substring/indexOf(".git") logic
(references: repoUrl, stringSearch, trailingSlashSearch, repoPath) with this
normalized value and keep the existing fallback ("tsparticles/plugin-template")
if normalization yields an empty string.

Comment on lines +148 to +151
{
from: /preset: "template"/g,
to: `preset: "${camelizedName}`,
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing closing quote in the replacement string.

Line 150 has an incomplete string template - the closing quote is missing after ${camelizedName}.

🐛 Proposed fix
       {
         from: /preset: "template"/g,
-        to: `preset: "${camelizedName}`,
+        to: `preset: "${camelizedName}"`,
       },
📝 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
{
from: /preset: "template"/g,
to: `preset: "${camelizedName}`,
},
{
from: /preset: "template"/g,
to: `preset: "${camelizedName}"`,
},
🤖 Prompt for AI Agents
In `@src/create/preset/create-preset.ts` around lines 148 - 151, The replacement
string for the RegExp in create-preset.ts is missing the closing
brace/quote/backtick; update the `to` value so the template interpolation is
closed and the string is valid — e.g. change `to: \`preset: "${camelizedName}\``
to `to: \`preset: "${camelizedName}"\`` (ensure `${camelizedName}` is closed and
the surrounding backtick/string quote is present).

Comment on lines +135 to +138
{
from: /shape\.type: "template"/g,
to: `shape.type: "${camelizedName}`,
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing closing quote in the replacement string.

Line 137 has an incomplete string template - the closing quote is missing after ${camelizedName}.

🐛 Proposed fix
       {
         from: /shape\.type: "template"/g,
-        to: `shape.type: "${camelizedName}`,
+        to: `shape.type: "${camelizedName}"`,
       },
📝 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
{
from: /shape\.type: "template"/g,
to: `shape.type: "${camelizedName}`,
},
{
from: /shape\.type: "template"/g,
to: `shape.type: "${camelizedName}"`,
},
🤖 Prompt for AI Agents
In `@src/create/shape/create-shape.ts` around lines 135 - 138, The replacement
string in the mapping object for shape.type is missing the closing brace/quote
around the template variable; update the `to` value used alongside the `from:
/shape\.type: "template"/g` entry so the template variable `camelizedName` is
properly closed (i.e., ensure `${camelizedName}` is fully terminated inside the
string literal) in the mapping near create-shape.ts so the replacement produces
a valid string `shape.type: "..."`.

@coderabbitai coderabbitai bot mentioned this pull request Feb 24, 2026
This was referenced Mar 2, 2026
This was referenced Mar 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants