-
-
Notifications
You must be signed in to change notification settings - Fork 0
3.0.0 #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
3.0.0 #62
Conversation
WalkthroughConvert repo from CommonJS to ESM: replace CJS configs/scripts with ESM equivalents, add eslint.config.js and vitest config, upgrade Node/CI/pnpm and many dependencies, migrate tests to Vitest, broaden TypeScript targets/moduleResolution, normalize imports to explicit .js, adjust path joins, and improve runtime package.json handling and error objects. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Dev as Developer
participant Repo as Repository
participant CI as GitHub Actions (Node 22)
participant PNPM as pnpm (v10)
participant CLI as CLI (ESM)
participant ESLint as ESLint (eslint.config.js)
participant TSC as TypeScript (NodeNext)
participant Bundler as Webpack (ESM)
participant Test as Vitest
Dev->>Repo: push/PR
CI->>Repo: checkout (actions/checkout@v5)
CI->>PNPM: setup pnpm v10
CI->>Test: run tests (vitest)
CI->>CLI: run build scripts
CLI->>ESLint: lint project (eslint.config.js)
ESLint-->>CLI: lint results
CLI->>TSC: typecheck (tsconfig NodeNext)
TSC-->>CLI: diagnostics
CLI->>Bundler: run webpack (ESM config reads package.json at runtime)
Bundler-->>CLI: stats/errors (normalized as Error objects)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
✨ Finishing Touches🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
tests/file-utils.test.ts (2)
97-99
: Replace after with afterAllFixes the runtime error.
-after(async () => { +afterAll(async () => { await fs.remove(baseDir); -}); +});
11-15
: Avoid async describe and top-level awaits; move setup into hooksAsync describe callbacks and awaits at definition time are brittle. Use beforeAll/beforeEach for setup.
-describe("file-utils", async () => { - const baseDir = path.join(__dirname, "tmp-files"); - - await fs.ensureDir(baseDir); +describe("file-utils", () => { + const baseDir = path.join(testDir, "tmp-files"); + beforeAll(async () => { + await fs.ensureDir(baseDir); + }); @@ - describe("replace tokens in files", async () => { - fs.writeFileSync(path.join(baseDir, "files1.txt"), "test"); - fs.writeFileSync(path.join(baseDir, "files2.txt"), "test"); - - await replaceTokensInFiles([{ + describe("replace tokens in files", () => { + beforeAll(async () => { + fs.writeFileSync(path.join(baseDir, "files1.txt"), "test"); + fs.writeFileSync(path.join(baseDir, "files2.txt"), "test"); + await replaceTokensInFiles([{ path: path.join(baseDir, "files1.txt"), tokens: [{ from: "test", to: "test1", }], }, { path: path.join(baseDir, "files2.txt"), tokens: [{ from: "test", to: "test2", }], - }]); + }]); + }); @@ - describe("replace tokens in file", async () => { - fs.writeFileSync(path.join(baseDir, "file1.txt"), "test"); - - await replaceTokensInFile({ + describe("replace tokens in file", () => { + beforeAll(async () => { + fs.writeFileSync(path.join(baseDir, "file1.txt"), "test"); + await replaceTokensInFile({ path: path.join(baseDir, "file1.txt"), tokens: [{ from: "test", to: "test1", }], - }); + }); + }); @@ - describe("get destination dir", async () => { - const destDir = await getDestinationDir(path.join(baseDir, "baz")); + describe("get destination dir", () => { + let destDir: string; + beforeAll(async () => { + destDir = await getDestinationDir(path.join(baseDir, "baz")); + });Also applies to: 16-33, 43-53, 61-63
.github/workflows/node.js-ci.yml (1)
36-37
: Replace deprecated set-output with GITHUB_OUTPUT.Prevents deprecation warnings and future breaks.
- - name: Get pnpm store directory + - name: Get pnpm store directory id: pnpm-cache run: | - echo "::set-output name=pnpm_cache_dir::$(pnpm store path)" + echo "pnpm_cache_dir=$(pnpm store path)" >> "$GITHUB_OUTPUT"- - name: Get pnpm store directory + - name: Get pnpm store directory id: pnpm-cache run: | - echo "::set-output name=pnpm_cache_dir::$(pnpm store path)" + echo "pnpm_cache_dir=$(pnpm store path)" >> "$GITHUB_OUTPUT"Also applies to: 74-75
src/build/build-bundle.ts (1)
38-49
: Don’t resolve after rejecting; also normalize error messages.Currently it can hit resolve(true) even after reject. Add return; handle string/unknown errors.
- if (stats.hasErrors()) { - console.error(info.errors); - - reject(new Error(info.errors?.map(error => error.message).join("\n"))); - } + if (stats.hasErrors()) { + console.error(info.errors); + const msg = + info.errors?.map((e: unknown) => + typeof e === "string" ? e : (e as { message?: string }).message ?? String(e), + ).join("\n") || "Unknown webpack error"; + reject(new Error(msg)); + return; + }src/build/build-tsc.ts (1)
117-121
: Guard JSON parsing to fail gracefully with an explicit exit code.A malformed tsconfig will currently throw and abort the process. Parse errors should return ParseError consistently.
Apply:
- if (!options && data) { - options = JSON.parse(data); - } + if (!options && data) { + try { + options = JSON.parse(data); + } catch (e) { + console.error("Failed parsing tsconfig JSON:", e); + return ExitCodes.ParseError; + } + }
🧹 Nitpick comments (32)
files/empty-project/package.dist.json (1)
5-5
: ESM-only distro: confirm intentional CJS drop.Setting "type": "module" with "main": "index.js" makes require("@tsparticles/empty-template") fail in Node. If that’s intended, consider adding an explicit exports for clarity; if not, provide a CJS fallback (.cjs).
Optional clarity:
"type": "module", + "exports": { + ".": { + "types": "./index.d.ts", + "import": "./index.js", + "default": "./index.js" + } + },src/tsconfig.json (1)
30-35
: Align TS module/resolution settings with ESMDetected legacy settings in both tsconfig files:
- src/tsconfig.json (lines 30–35)
- tests/tsconfig.json (lines 4–5, 17–18)
Suggested changes:
--- a/src/tsconfig.json @@ - "module": "es2020", + "module": "ESNext", @@ - "moduleResolution": "node", + "moduleResolution": "Bundler", + "verbatimModuleSyntax": true,--- a/tests/tsconfig.json @@ - "module": "commonjs", + "module": "ESNext", @@ - "moduleResolution": "node", + "moduleResolution": "Bundler",Ensure your test runner supports ESM imports before migrating tests config.
src/utils/file-utils.ts (1)
75-85
: Return a trimmed UTF-8 string to avoid trailing newline issues.exec returns stdout with a trailing newline; trimming makes downstream comparisons stable and types unambiguous.
- exec("git config --get remote.origin.url", (error, stdout) => { + exec("git config --get remote.origin.url", { encoding: "utf8" }, (error, stdout) => { @@ - resolve(stdout); + resolve(stdout.trim());tests/tsconfig.json (2)
6-6
: Align module settings with ESM toolchain.
Given the repo migrates to ESM/Node 22, considermodule: "nodenext"
andmoduleResolution: "nodenext"
(ormoduleResolution: "bundler"
for Vitest) to avoid subtle resolution mismatches in tests.
24-24
: Exclude generated artifacts to keep TS fast and avoid noisy types.
Add excludes for temp dirs and build outputs under tests to prevent typechecking generated template projects.Apply this diff:
- "include": ["**/*.ts"] + "include": ["**/*.ts"], + "exclude": ["tmp-files/**", "**/node_modules/**", "**/dist/**", "**/build/**"]pnpm-workspace.yaml (1)
1-3
: Validate PNPM’s onlyBuiltDependencies behavior in CI.
Good optimization, but confirm PNPM 10 supports and applies this at workspace root as intended, and that no other deps require build in your workflows. If you rely on workspace package discovery, ensurepackages:
is defined elsewhere or intentionally omitted.tests/create-plugin.test.ts (1)
8-8
: Nit: avoid redundant resolve(join()).
Preferpath.resolve(__dirname, "tmp-files", "foo-plugin")
.tests/create-preset.test.ts (1)
8-8
: Nit: simplify path construction.
Usepath.resolve(__dirname, "tmp-files", "foo-preset")
.tsconfig.json (1)
18-22
: Include globs look fine; add excludes to avoid checking generated projects.
Since tests now generate projects under tests/tmp-files, exclude them from the root TS program.Apply this diff:
"include": [ "src/**/*", "tests/**/*", "files/**/*" - ] + ], + "exclude": [ + "tests/tmp-files/**", + "**/node_modules/**", + "**/dist/**", + "**/build/**" + ]Additionally, consider removing legacy Mocha/Chai globals from types to prevent conflicts with Vitest:
// tsconfig.json (types section) "types": [ "node", "fs-extra", "klaw", "madge", "prompts", "eslint" // removed: "chai", "mocha" ]tsconfig.eslint.json (1)
1-7
: Ensure ESLint actually uses this project TSConfig for type-aware lintingIf the root flat config points parserOptions.project to a different tsconfig, tests may miss type info. Consider pointing ESLint to this file (./tsconfig.eslint.json). Also safe to set noEmit here.
{ "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, "include": [ "src/**/*", "tests/**/*" ] }files/empty-project/eslint.config.js (1)
1-6
: Flat config is fine; tiny simplification possibleYou can export the preset directly—defineConfig wrapper isn’t needed for a single item.
-import tsParticlesESLintConfig from "@tsparticles/eslint-config"; -import { defineConfig } from "eslint/config"; - -export default defineConfig([ - tsParticlesESLintConfig, -]); +import tsParticlesESLintConfig from "@tsparticles/eslint-config"; +export default tsParticlesESLintConfig;vitest.config.ts (1)
3-10
: LGTM; add a couple of quality-of-life defaults (optional)Consider including .spec files and enabling automatic mock cleanup.
export default defineConfig({ test: { globals: true, environment: "node", - include: ["tests/**/*.test.ts"], - testTimeout: 30000 + include: ["tests/**/*.{test,spec}.ts"], + testTimeout: 30000, + clearMocks: true, + restoreMocks: true } });tests/file-utils.test.ts (2)
74-88
: Use rejects.toThrow instead of manual try/catch in async error testCleaner, and assertions run even if the promise resolves unexpectedly.
- it("should throw exception", async () => { - await fs.writeFile(path.join(baseDir, "baz", "tmp.txt"), ""); - - let ex = false; - - try { - await getDestinationDir(path.join(baseDir, "baz")); - - console.log("never"); - } catch { - ex = true; - } - - expect(ex).toBe(true); - }); + it("should throw exception", async () => { + await fs.writeFile(path.join(baseDir, "baz", "tmp.txt"), ""); + await expect(getDestinationDir(path.join(baseDir, "baz"))) + .rejects.toThrow("Destination folder already exists and is not empty"); + });
1-4
: ESM-safe __dirname usage (optional)Under pure ESM, __dirname isn’t defined. Make tests robust by deriving it from import.meta.url and avoiding reliance on Node globals.
-import { describe, it, expect, after } from "vitest"; +import { describe, it, expect, beforeAll, afterAll } from "vitest"; import fs from "fs-extra"; -import path from "path"; +import path from "path"; +import { fileURLToPath } from "node:url"; @@ -describe("file-utils", async () => { - const baseDir = path.join(__dirname, "tmp-files"); +const testDir = path.dirname(fileURLToPath(import.meta.url)); + +describe("file-utils", () => { + const baseDir = path.join(testDir, "tmp-files");Also applies to: 11-13
src/build/build-circular-deps.ts (1)
23-29
: LGTM; tiny improvement to support .mts filesGiven the ESM migration, include .mts in madge extensions so circular checks see module-suffixed TS files too.
- fileExtensions: ["ts"], + fileExtensions: ["ts", "mts"],tests/create-shape.test.ts (2)
16-16
: Ensure cleanup runs even on failure.Wrap test bodies in try/finally to always remove tmp dirs.
- await fs.remove(destDir); + } finally { + await fs.remove(destDir); + }Also applies to: 28-28
8-9
: __dirname under ESM may be undefined—verify test runtime.If tests run as ESM, switch to fileURLToPath(import.meta.url) to compute __dirname.
+import { fileURLToPath } from "url"; +const __dirname = path.dirname(fileURLToPath(import.meta.url));Also applies to: 20-21
src/build/build-eslint.ts (1)
28-31
: Minor: guard undefined line/column.Not required, but safer formatting avoids “undefined” when positions are missing.
- .map(m => `${t.filePath} (${m.line.toString()},${m.column.toString()}): ${m.message}`) + .map(m => `${t.filePath} (${m.line ?? "?"},${m.column ?? "?"}): ${m.message}`)eslint.config.js (2)
11-17
: Place sourceType at languageOptions level (Flat Config).sourceType should be a sibling of parserOptions, not inside it.
- languageOptions: { - parserOptions: { - project: [path.resolve(__dirname, "src/tsconfig.json")], - tsconfigRootDir: __dirname, - sourceType: "module" - } - }, + languageOptions: { + sourceType: "module", + parserOptions: { + project: [path.resolve(__dirname, "src/tsconfig.json")], + tsconfigRootDir: __dirname + } + },
8-22
: Optional: scope overrides with files globs.If you want this config to target only src TS files, add files: ["src/**/*.{ts,tsx}"] to the override block.
export default defineConfig([ tsParticlesESLintConfig, { + files: ["src/**/*.{ts,tsx}"], languageOptions: {
.github/workflows/node.js-ci.yml (2)
60-61
: Align pnpm/action-setup versions.Use v4 in both jobs for consistency.
- - uses: pnpm/action-setup@v3 + - uses: pnpm/action-setup@v4
45-47
: Reproducibility: prefer frozen lockfile in CI.Avoids drift from lockfile and surprise deps.
- - run: pnpm install --no-frozen-lockfile + - run: pnpm install --frozen-lockfileAlso applies to: 83-85
src/build/build-bundle.ts (1)
26-33
: Minor: reuse Error instance without shadowing.Not harmful, but avoid reusing the name err for clarity.
- const err = new Error("No stats returned from webpack"); + const noStatsErr = new Error("No stats returned from webpack"); - console.error(err); - reject(err); + console.error(noStatsErr); + reject(noStatsErr);files/empty-project/webpack.config.js (1)
8-9
: Consider importing JSON instead of sync FS read (minor).Node supports JSON modules; this avoids sync I/O and a manual parse.
Example change:
-import fs from 'fs'; +import fs from 'fs'; +// or, when feasible +// import pkg from './package.json' assert { type: 'json' }; -const __filename = fileURLToPath(import.meta.url), - __dirname = path.dirname(__filename), - pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')), - version = pkg.version; +const __filename = fileURLToPath(import.meta.url), + __dirname = path.dirname(__filename), + pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')), + version = pkg.version; // If using JSON import: // const __filename = fileURLToPath(import.meta.url), // __dirname = path.dirname(__filename), // { version } = pkg;src/build/build-tsc.ts (3)
128-131
: Surface tsconfig parse diagnostics to the console.Without printing, callers only see a non-zero exit code.
Apply:
- if (parsed.errors.length) { - return ExitCodes.ParseError; - } + if (parsed.errors.length) { + const host = { + getCurrentDirectory: ts.sys.getCurrentDirectory, + getNewLine: () => ts.sys.newLine, + getCanonicalFileName: (f: string) => f, + }; + + console.error(ts.formatDiagnosticsWithColorAndContext(parsed.errors, host)); + return ExitCodes.ParseError; + }
138-156
: Prefer TypeScript’s formatted diagnostics for consistency (optional).Manual line/column formatting works, but
formatDiagnosticsWithColorAndContext
yields clearer, standardized output.If you keep the manual formatter, consider removing the superfluous
.toString()
calls on numbers for brevity.
160-160
: Avoid locale-dependent exit code formatting in logs (tiny nit).
toLocaleString()
can add separators; not ideal for copy/paste or parsing.- console.log(`TSC for ${type} done with exit code: '${exitCode.toLocaleString()}'.`); + console.log(`TSC for ${type} done with exit code: ${exitCode}.`);src/build/build.ts (1)
112-127
: Stat messages are fine; consider thresholds and units (optional).Switching warn/info based on increase is nice. Optionally add a threshold (e.g., >1KB) and human-readable units to reduce noise.
scripts/postversion.js (1)
12-14
: Use English in code comments for consistency.Aligns with the rest of the codebase and helps external contributors.
- // usa readJson/writeJson di fs-extra per evitare parse manuale e problemi di typing + // Use fs-extra readJson/writeJson to avoid manual parsing and typing issuespackage.json (3)
24-31
: Script names vs behavior (minor).
build:ts:cjs
suggests CommonJS, but the repo is ESM ("type": "module"
). Consider renaming for clarity.- "build:ts:cjs": "tsc -p src", + "build:ts:esm": "tsc -p src", + "build:ts": "pnpm run build:ts:esm",
14-17
: Prettier scripts only target top-level src files.Globs like
./src/*
miss nested folders.- "prettify:ci:src": "prettier --check ./src/*", - "prettify:src": "prettier --write ./src/*", + "prettify:ci:src": "prettier --check \"src/**/*\"", + "prettify:src": "prettier --write \"src/**/*\"",
1-81
: Document runtime requirements (optional).Given ESM and toolchain, declare supported Node/PNPM versions to prevent install/run surprises.
"publishConfig": { "access": "public" }, + "engines": { + "node": ">=22" + }, + "packageManager": "pnpm@10"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (29)
.eslintignore
(0 hunks).eslintrc.js
(0 hunks).github/workflows/node.js-ci.yml
(2 hunks)eslint.config.js
(1 hunks)files/empty-project/.eslintignore
(0 hunks)files/empty-project/.eslintrc.js
(0 hunks)files/empty-project/eslint.config.js
(1 hunks)files/empty-project/package.dist.json
(1 hunks)files/empty-project/package.json
(2 hunks)files/empty-project/webpack.config.js
(1 hunks)package.json
(2 hunks)pnpm-workspace.yaml
(1 hunks)scripts/postversion.js
(1 hunks)src/build/build-bundle.ts
(2 hunks)src/build/build-circular-deps.ts
(1 hunks)src/build/build-eslint.ts
(3 hunks)src/build/build-tsc.ts
(5 hunks)src/build/build.ts
(3 hunks)src/tsconfig.json
(1 hunks)src/utils/file-utils.ts
(1 hunks)tests/create-plugin.test.ts
(2 hunks)tests/create-preset.test.ts
(2 hunks)tests/create-shape.test.ts
(2 hunks)tests/file-utils.test.ts
(4 hunks)tests/string-utils.test.ts
(1 hunks)tests/tsconfig.json
(2 hunks)tsconfig.eslint.json
(1 hunks)tsconfig.json
(1 hunks)vitest.config.ts
(1 hunks)
💤 Files with no reviewable changes (4)
- .eslintrc.js
- .eslintignore
- files/empty-project/.eslintignore
- files/empty-project/.eslintrc.js
🧰 Additional context used
🧬 Code graph analysis (8)
tests/create-preset.test.ts (1)
src/create/preset/create-preset.ts (1)
createPresetTemplate
(180-204)
tests/string-utils.test.ts (1)
src/utils/string-utils.ts (3)
capitalize
(7-18)camelize
(26-28)dash
(35-41)
tests/create-shape.test.ts (1)
src/create/shape/create-shape.ts (1)
createShapeTemplate
(161-184)
scripts/postversion.js (1)
eslint.config.js (1)
__dirname
(3-3)
tests/file-utils.test.ts (1)
src/utils/file-utils.ts (2)
getDestinationDir
(49-65)getRepositoryUrl
(70-86)
src/build/build.ts (1)
src/build/build-prettier.ts (3)
prettifyReadme
(144-177)prettifyPackageJson
(60-94)prettifyPackageDistJson
(101-137)
tests/create-plugin.test.ts (1)
src/create/plugin/create-plugin.ts (1)
createPluginTemplate
(162-185)
files/empty-project/webpack.config.js (2)
eslint.config.js (1)
__dirname
(3-3)scripts/postversion.js (1)
pkg
(13-14)
🪛 GitHub Actions: Node.js CI
tests/create-preset.test.ts
[error] 1-1: Test timed out in 30000ms for 'should have created the preset project'
tests/string-utils.test.ts
[error] 1-1: Error: No test found in suite empty string
tests/create-shape.test.ts
[error] 1-1: Test timed out in 30000ms for 'should have created the shape project'
tests/file-utils.test.ts
[error] 97-97: TypeError: after is not a function
tests/create-plugin.test.ts
[error] 1-1: Test timed out in 30000ms for 'should have created the plugin project'
[error] 1-1: Test timed out in 30000ms for 'should have created the plugin project, w/ repo'
🔇 Additional comments (9)
files/empty-project/package.json (1)
87-87
: Dev bump to @tsparticles/cli v3 looks fine.Assuming CI is already on Node 22 (as per PR), this is good.
Please confirm the template’s engines (if any) and CI matrix include Node >=20/22 to match CLI v3 requirements.
tests/tsconfig.json (1)
6-6
: Vitest types switch looks good.
The migration from Mocha/Chai to Vitest types is correct.src/build/build-eslint.ts (1)
15-15
: Use Flat Config programmatically: overrideConfig + useFlatConfig (not baseConfig).Current code passes a Flat config array to baseConfig (legacy); this can misconfigure ESLint under v9.
- const eslint = new ESLint({ baseConfig: defineConfig([tsParticlesESLintConfig]), fix: !ci }); + const eslint = new ESLint({ + useFlatConfig: true, + overrideConfig: defineConfig([tsParticlesESLintConfig]), + fix: !ci, + });If tsParticlesESLintConfig already is a defineConfig([...]) result, you can pass it directly to overrideConfig without wrapping again.
Likely an incorrect or invalid review comment.
files/empty-project/webpack.config.js (1)
6-9
: ESM dirname/filename computation looks correct.The migration away from CJS globals is handled properly; reading the local package.json is fine for a config-time read.
src/build/build-tsc.ts (1)
4-10
: Good call switching from const enum to enum for ESM compatibility.Prevents inlining pitfalls and keeps runtime symbols available under modern module targets.
src/build/build.ts (1)
57-61
: Prettify step gating is correct.Short-circuiting on failure via
canContinue
is clear and predictable.scripts/postversion.js (1)
16-23
: Async flow and error handling look solid.Paths resolve correctly; devDependencies is ensured; writeJson uses spacing. LGTM.
package.json (2)
3-3
: Version/tag alignment check.The PR title says 3.0.0, but version is 3.0.1. Confirm intended release version and tag.
Would you like me to open a follow-up to sync the PR title/changelog with 3.0.1?
79-80
: Confirm intent of main pointing to ESLint config.If consumers import this package for its CLI only,
main
may be unnecessary; if you intend to expose the config, consider adding anexports
map.Optionally:
{ "exports": { ".": "./eslint.config.js" } }
package.json
Outdated
"@babel/core": "^7.28.3", | ||
"@tsparticles/cli": "^2.3.3", | ||
"@tsparticles/engine": "^3.9.1", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove self-dependency from devDependencies.
The package lists itself as a devDependency, which can cause resolution issues.
"devDependencies": {
- "@babel/core": "^7.28.3",
- "@tsparticles/cli": "^2.3.3",
+ "@babel/core": "^7.28.3",
📝 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.
"@babel/core": "^7.28.3", | |
"@tsparticles/cli": "^2.3.3", | |
"@tsparticles/engine": "^3.9.1", | |
"@babel/core": "^7.28.3", | |
"@tsparticles/engine": "^3.9.1", |
🤖 Prompt for AI Agents
package.json around lines 57-59: remove the package's own name from
devDependencies (the self-dependency entry) so the project does not list itself
as a devDependency; delete that dependency line, save package.json, then run
your package manager (npm install or yarn install) to update node_modules and
the lockfile and verify package.json remains valid.
canContinue = | ||
(await prettifyReadme(basePath, ci)) && | ||
(await prettifyPackageJson(basePath, ci)) && | ||
(await prettifyPackageDistJson(basePath, ci)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Skip writing package.dist.json in CI mode to keep CI read-only.
prettifyPackageDistJson
currently writes regardless of ci
, which can dirty the tree during --ci
runs.
Apply:
- canContinue =
- (await prettifyReadme(basePath, ci)) &&
- (await prettifyPackageJson(basePath, ci)) &&
- (await prettifyPackageDistJson(basePath, ci));
+ canContinue =
+ (await prettifyReadme(basePath, ci)) &&
+ (await prettifyPackageJson(basePath, ci)) &&
+ (!ci ? await prettifyPackageDistJson(basePath, ci) : true);
📝 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.
canContinue = | |
(await prettifyReadme(basePath, ci)) && | |
(await prettifyPackageJson(basePath, ci)) && | |
(await prettifyPackageDistJson(basePath, ci)); | |
} | |
canContinue = | |
(await prettifyReadme(basePath, ci)) && | |
(await prettifyPackageJson(basePath, ci)) && | |
(!ci ? await prettifyPackageDistJson(basePath, ci) : true); | |
} |
🤖 Prompt for AI Agents
In src/build/build.ts around lines 90 to 94, the call to prettifyPackageDistJson
currently runs during CI and mutates files; update the logic to skip
prettifyPackageDistJson when ci is true (only run it when ci is false) so CI
mode remains read-only. Modify the condition so canContinue awaits
prettifyReadme and prettifyPackageJson always, but only awaits
prettifyPackageDistJson when ci is false (e.g., || short-circuit or conditional
call), ensuring no writes occur in CI and preserving the original success
boolean semantics.
@@ -1,18 +1,17 @@ | |||
import { describe, it } from "mocha"; | |||
import { expect } from "chai"; | |||
import { describe, it, expect } from "vitest"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Provide a fast path in the generator to skip install/build.
Expose a skipInstallBuild
option (default false) so tests don’t spawn package installs/builds.
Here’s a minimal pattern to apply in src/create/plugin/create-plugin.ts
(illustrative):
export async function createPluginTemplate(
name: string,
description: string,
repoUrl: string,
destPath: string,
opts: { skipInstallBuild?: boolean } = {}
): Promise<void> {
// ...copy work...
if (!opts.skipInstallBuild) {
await runInstall(destPath);
await runBuild(destPath);
}
}
Then in tests:
await createPluginTemplate("foo", "Foo", "", destDir, { skipInstallBuild: true });
🧰 Tools
🪛 GitHub Actions: Node.js CI
[error] 1-1: Test timed out in 30000ms for 'should have created the plugin project'
[error] 1-1: Test timed out in 30000ms for 'should have created the plugin project, w/ repo'
🤖 Prompt for AI Agents
In tests/create-plugin.test.ts around line 1, the test suite currently invokes
the plugin generator which spawns package installs/builds making tests slow; add
a fast-path option to the generator and update tests to use it. In
src/create/plugin/create-plugin.ts (or the module exporting the generator) add
an opts parameter typed { skipInstallBuild?: boolean } with default false, and
wrap the install/build calls in if (!opts.skipInstallBuild) { await
runInstall(...); await runBuild(...); } so behavior is unchanged by default.
Update any exported types/signatures and call sites accordingly, and change
tests/create-plugin.test.ts to call the generator with { skipInstallBuild: true
} to avoid running installs/builds during tests. Ensure TypeScript typings and
all internal uses propagate the optional param to avoid type errors.
tests/create-plugin.test.ts
Outdated
it("should have created the plugin project", async () => { | ||
const destDir = path.resolve(path.join(__dirname, "tmp-files", "foo-plugin")); | ||
|
||
await createPluginTemplate("foo", "Foo", "", destDir); | ||
|
||
const pkgInfo = await fs.readJSON(path.join(destDir, "package.json")); | ||
|
||
expect(pkgInfo.name).to.be.equal("tsparticles-plugin-foo"); | ||
expect(pkgInfo.name).toBe("tsparticles-plugin-foo"); | ||
|
||
await fs.remove(destDir); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix CI timeouts by avoiding long installs/builds in unit tests.
Both tests are timing out (30s) due to runInstall
/runBuild
. Make them serial, add per-test timeouts, and ensure cleanup with finally.
Apply this diff:
-import { describe, it, expect } from "vitest";
+import { describe, it, expect } from "vitest";
import { createPluginTemplate } from "../src/create/plugin/create-plugin";
import path from "path";
import fs from "fs-extra";
-describe("create-plugin", () => {
- it("should have created the plugin project", async () => {
- const destDir = path.resolve(path.join(__dirname, "tmp-files", "foo-plugin"));
-
- await createPluginTemplate("foo", "Foo", "", destDir);
-
- const pkgInfo = await fs.readJSON(path.join(destDir, "package.json"));
-
- expect(pkgInfo.name).toBe("tsparticles-plugin-foo");
-
- await fs.remove(destDir);
- });
+describe.sequential("create-plugin", () => {
+ it("should have created the plugin project", async () => {
+ const destDir = path.resolve(__dirname, "tmp-files", "foo-plugin");
+ try {
+ await createPluginTemplate("foo", "Foo", "", destDir);
+ const pkgInfo = await fs.readJSON(path.join(destDir, "package.json"));
+ expect(pkgInfo.name).toBe("tsparticles-plugin-foo");
+ } finally {
+ await fs.remove(destDir);
+ }
+ }, 180000);
- it("should have created the plugin project, w/ repo", async () => {
- const destDir = path.resolve(path.join(__dirname, "tmp-files", "bar-plugin"));
-
- await createPluginTemplate("bar", "Bar", "https://github.com/matteobruni/tsparticles", destDir);
-
- const pkgInfo = await fs.readJSON(path.join(destDir, "package.json"));
-
- expect(pkgInfo.name).toBe("tsparticles-plugin-bar");
-
- await fs.remove(destDir);
- });
+ it("should have created the plugin project, w/ repo", async () => {
+ const destDir = path.resolve(__dirname, "tmp-files", "bar-plugin");
+ try {
+ await createPluginTemplate("bar", "Bar", "https://github.com/matteobruni/tsparticles", destDir);
+ const pkgInfo = await fs.readJSON(path.join(destDir, "package.json"));
+ expect(pkgInfo.name).toBe("tsparticles-plugin-bar");
+ } finally {
+ await fs.remove(destDir);
+ }
+ }, 180000);
});
Follow-up (strongly recommended):
- Inject an option to skip install/build in template generators and use it in tests (see next comment).
- Alternatively, raise
test.testTimeout
/hookTimeout
in vitest.config.ts for this suite.
Also applies to: 19-29
🤖 Prompt for AI Agents
In tests/create-plugin.test.ts around lines 7-17 (and similarly 19-29), the
tests are timing out because they perform long installs/builds; make the tests
run serially, add per-test timeouts, and ensure cleanup runs regardless of
failures: convert the concurrent tests to run sequentially (use
sequential/single test execution or avoid parallel test runner features), wrap
the createPluginTemplate call and assertions in try/finally and move
fs.remove(destDir) into the finally block, set a shorter per-test timeout (e.g.,
pass a timeout param to the test or use the test framework's per-test timeout
API) and avoid invoking runInstall/runBuild in unit tests by passing an option
to skip install/build to createPluginTemplate (or stub/mock those calls) so
tests complete quickly.
tests/create-preset.test.ts
Outdated
it("should have created the preset project", async () => { | ||
const destDir = path.resolve(path.join(__dirname, "tmp-files", "foo-preset")); | ||
|
||
await createPresetTemplate("foo", "Foo", "", destDir); | ||
|
||
const pkgInfo = await fs.readJSON(path.join(destDir, "package.json")); | ||
|
||
expect(pkgInfo.name).to.be.equal("tsparticles-preset-foo"); | ||
expect(pkgInfo.name).toBe("tsparticles-preset-foo"); | ||
|
||
await fs.remove(destDir); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Same timeout fix pattern as plugin tests.
Serialize, add per-test timeout, and guard cleanup.
Apply this diff:
-import { describe, it, expect } from "vitest";
+import { describe, it, expect } from "vitest";
import { createPresetTemplate } from "../src/create/preset/create-preset";
import path from "path";
import fs from "fs-extra";
-describe("create-preset", () => {
- it("should have created the preset project", async () => {
- const destDir = path.resolve(path.join(__dirname, "tmp-files", "foo-preset"));
-
- await createPresetTemplate("foo", "Foo", "", destDir);
-
- const pkgInfo = await fs.readJSON(path.join(destDir, "package.json"));
-
- expect(pkgInfo.name).toBe("tsparticles-preset-foo");
-
- await fs.remove(destDir);
- });
+describe.sequential("create-preset", () => {
+ it("should have created the preset project", async () => {
+ const destDir = path.resolve(__dirname, "tmp-files", "foo-preset");
+ try {
+ await createPresetTemplate("foo", "Foo", "", destDir);
+ const pkgInfo = await fs.readJSON(path.join(destDir, "package.json"));
+ expect(pkgInfo.name).toBe("tsparticles-preset-foo");
+ } finally {
+ await fs.remove(destDir);
+ }
+ }, 180000);
- it("should have created the preset project, w/ repo", async () => {
- const destDir = path.resolve(path.join(__dirname, "tmp-files", "bar-preset"));
-
- await createPresetTemplate("bar", "Bar", "https://github.com/matteobruni/tsparticles", destDir);
-
- const pkgInfo = await fs.readJSON(path.join(destDir, "package.json"));
-
- expect(pkgInfo.name).toBe("tsparticles-preset-bar");
-
- await fs.remove(destDir);
- });
+ it("should have created the preset project, w/ repo", async () => {
+ const destDir = path.resolve(__dirname, "tmp-files", "bar-preset");
+ try {
+ await createPresetTemplate("bar", "Bar", "https://github.com/matteobruni/tsparticles", destDir);
+ const pkgInfo = await fs.readJSON(path.join(destDir, "package.json"));
+ expect(pkgInfo.name).toBe("tsparticles-preset-bar");
+ } finally {
+ await fs.remove(destDir);
+ }
+ }, 180000);
});
Follow-up: add the same skipInstallBuild
option to createPresetTemplate
and use it in tests.
Also applies to: 19-29
🤖 Prompt for AI Agents
In tests/create-preset.test.ts around lines 7 to 17 (and similarly for lines 19
to 29), the test needs the same timeout/serialization and cleanup guards as the
plugin tests: make the test run sequentially by ensuring it doesn't rely on
shared async state, add a per-test timeout (e.g. jest.setTimeout or the test
timeout parameter) to avoid hanging, wrap the cleanup (fs.remove) in a finally
block so destDir is removed even if assertions fail, and call
createPresetTemplate with a new skipInstallBuild boolean option (add this option
to createPresetTemplate signature and implementation) then pass
skipInstallBuild: true from the test to avoid installs/builds during testing.
tests/create-shape.test.ts
Outdated
it("should have created the shape project", async () => { | ||
const destDir = path.resolve(path.join(__dirname, "tmp-files", "foo-shape")); | ||
|
||
await createShapeTemplate("foo", "Foo", "", destDir); | ||
|
||
const pkgInfo = await fs.readJSON(path.join(destDir, "package.json")); | ||
|
||
expect(pkgInfo.name).to.be.equal("tsparticles-shape-foo"); | ||
expect(pkgInfo.name).toBe("tsparticles-shape-foo"); | ||
|
||
await fs.remove(destDir); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix flakiness: these tests run install/build and hit the 30s timeout.
Either stub install/build or raise the per-test timeout. Minimal fix below.
- it("should have created the shape project", async () => {
+ it("should have created the shape project", async () => {
const destDir = path.resolve(path.join(__dirname, "tmp-files", "foo-shape"));
await createShapeTemplate("foo", "Foo", "", destDir);
const pkgInfo = await fs.readJSON(path.join(destDir, "package.json"));
expect(pkgInfo.name).toBe("tsparticles-shape-foo");
await fs.remove(destDir);
- });
+ }, 180_000);
Alternative (preferred): mock the heavy steps inside createShapeTemplate so unit tests don’t perform network/IO. I can provide a vi.mock-based approach if you want.
📝 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.
it("should have created the shape project", async () => { | |
const destDir = path.resolve(path.join(__dirname, "tmp-files", "foo-shape")); | |
await createShapeTemplate("foo", "Foo", "", destDir); | |
const pkgInfo = await fs.readJSON(path.join(destDir, "package.json")); | |
expect(pkgInfo.name).to.be.equal("tsparticles-shape-foo"); | |
expect(pkgInfo.name).toBe("tsparticles-shape-foo"); | |
await fs.remove(destDir); | |
}); | |
it("should have created the shape project", async () => { | |
const destDir = path.resolve(path.join(__dirname, "tmp-files", "foo-shape")); | |
await createShapeTemplate("foo", "Foo", "", destDir); | |
const pkgInfo = await fs.readJSON(path.join(destDir, "package.json")); | |
expect(pkgInfo.name).toBe("tsparticles-shape-foo"); | |
await fs.remove(destDir); | |
}, 180_000); |
🤖 Prompt for AI Agents
In tests/create-shape.test.ts around lines 7-17, the test runs install/build
steps and intermittently times out; either stub/mock the heavy install/build
operations inside createShapeTemplate so the unit test doesn’t perform
network/IO, or as a minimal change increase the per-test timeout (e.g., call
jest.setTimeout(...) or pass a longer timeout to the it(...) call) so the test
has enough time; preferred: replace or vi.mock the functions that perform
install/build within createShapeTemplate and assert they were called, keeping
the test fast and deterministic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
src/create/preset/create-preset.ts (1)
151-154
: Bug: missing closing quote in README token replacement.Generates invalid JSON/YAML snippet in README.
- { - from: /preset: "template"/g, - to: `preset: "${camelizedName}`, - }, + { + from: /preset: "template"/g, + to: `preset: "${camelizedName}"`, + },src/create/shape/create-shape.ts (3)
167-167
: __dirname will be undefined under ESM; compute it via import.meta.url.This will crash at runtime after the ESM migration.
Apply:
-import path from "path"; +import path from "path"; +import { fileURLToPath } from "url"; @@ -export async function createShapeTemplate( +export async function createShapeTemplate( @@ - const sourcePath = path.resolve(__dirname, "..", "..", "..", "files", "create-shape"); + const __dirname = path.dirname(fileURLToPath(import.meta.url)); + const sourcePath = path.resolve(__dirname, "..", "..", "..", "files", "create-shape");
100-108
: GitHub repoPath extraction breaks when URL lacks “.git”.For common URLs like https://github.com/user/repo, substring with endIndex -1 will produce a wrong path.
Use a safer extraction:
- stringSearch = "github.com", - trailingSlashSearch = "github.com/", - repoPath = repoUrl.includes(stringSearch) - ? repoUrl.substring( - repoUrl.indexOf(trailingSlashSearch) + trailingSlashSearch.length, - repoUrl.indexOf(".git"), - ) - : "tsparticles/shape-template"; + ghPrefix = "github.com/", + ghStart = repoUrl.indexOf(ghPrefix), + dotGit = repoUrl.indexOf(".git"), + repoPath = + ghStart >= 0 + ? repoUrl + .substring(ghStart + ghPrefix.length, dotGit > ghStart ? dotGit : undefined) + .replace(/\/$/, "") + : "tsparticles/shape-template";
133-135
: Token replacement is missing a closing quote.Generates invalid README snippet: shape.type: "foo
- { - from: /shape\.type: "template"/g, - to: `shape.type: "${camelizedName}`, - }, + { + from: /shape\.type: "template"/g, + to: `shape.type: "${camelizedName}"`, + },
♻️ Duplicate comments (1)
package.json (1)
57-59
: Remove self-dependency from devDependencies.Listing @tsparticles/cli as a devDependency can cause resolver loops.
"devDependencies": { - "@babel/core": "^7.28.3", - "@tsparticles/cli": "^2.3.3", + "@babel/core": "^7.28.3",
🧹 Nitpick comments (10)
package.json (4)
14-17
: Prettier scripts only cover top-level files; recurse into subfolders.- "prettify:ci:src": "prettier --check ./src/*", + "prettify:ci:src": "prettier --check \"src/**/*.{ts,tsx,js,jsx}\"", - "prettify:src": "prettier --write ./src/*", + "prettify:src": "prettier --write \"src/**/*.{ts,tsx,js,jsx}\"",
23-24
: Rename script alias to reflect ESM (nit)."build:ts:cjs" is misleading after the ESM migration.
- "build:ts": "pnpm run build:ts:cjs", - "build:ts:cjs": "tsc -p src", + "build:ts": "pnpm run build:ts:esm", + "build:ts:esm": "tsc -p src",
26-26
: Make chmod step cross-platform or move to a Node script.Raw chmod can fail on Windows.
- "build": "pnpm run clear:dist && pnpm run prettify:src && pnpm run lint && pnpm run compile && pnpm run circular-deps && pnpm run prettify:readme && chmod +x dist/cli.js && chmod +x dist/build/build.js && chmod +x dist/create/create.js && chmod +x dist/create/preset/preset.js", + "build": "pnpm run clear:dist && pnpm run prettify:src && pnpm run lint && pnpm run compile && pnpm run circular-deps && pnpm run prettify:readme && node scripts/make-executable.js",If you want, I can provide scripts/make-executable.js.
1-11
: Consider declaring supported Node and pnpm versions.Add engines to communicate runtime requirements.
"publishConfig": { "access": "public" }, + "engines": { + "node": ">=20", + "pnpm": ">=10" + },src/create/preset/create-preset.ts (1)
121-127
: Repo URL parsing is brittle; use URL().Handles non-.git HTTPS/SSH forms safely.
- repoPath = repoUrl.includes(stringSearch) - ? repoUrl.substring( - repoUrl.indexOf(trailingSlashSearch) + trailingSlashSearch.length, - repoUrl.indexOf(".git"), - ) - : "tsparticles/preset-template"; + repoPath = (() => { + try { + const u = new URL(repoUrl.replace(/^git\+/, "")); + return u.hostname === "github.com" ? u.pathname.replace(/^\/+|\.git$/g, "") : "tsparticles/preset-template"; + } catch { + return "tsparticles/preset-template"; + } + })();src/utils/template-utils.ts (1)
155-177
: Prefer pnpm when available; add timeouts to exec.Make installs respect local PM, and avoid hanging processes.
export async function runInstall(destPath: string): Promise<void> { - if (!(await lookpath("npm"))) { - return; - } + const pm = (await lookpath("pnpm")) ? "pnpm" : (await lookpath("npm")) ? "npm" : (await lookpath("yarn")) ? "yarn" : undefined; + if (!pm) return; @@ - exec( - "npm install", + exec( + pm === "yarn" ? "yarn install" : `${pm} install`, { cwd: destPath, + timeout: 15 * 60 * 1000, + maxBuffer: 10 * 1024 * 1024 }, error => { @@ export async function runBuild(destPath: string): Promise<void> { - if (!(await lookpath("npm"))) { - return; - } + const pm = (await lookpath("pnpm")) ? "pnpm" : (await lookpath("npm")) ? "npm" : (await lookpath("yarn")) ? "yarn" : undefined; + if (!pm) return; @@ - exec( - "npm run build", + exec( + pm === "yarn" ? "yarn build" : `${pm} run build`, { cwd: destPath, + timeout: 15 * 60 * 1000, + maxBuffer: 10 * 1024 * 1024 }, error => {Also applies to: 183-205
src/create/plugin/create-plugin.ts (1)
52-53
: Minor: redundant camelize calls; align with preset variant for readability.- const camelizedName = camelize(camelize(name, "-"), " "), + const camelizedName = camelize(name, "-", " "),Apply in both functions.
Also applies to: 77-78
src/create/plugin/plugin.ts (1)
38-45
: Gracefully handle prompt cancellation to avoid .trim() on undefined.If the user aborts prompts, destructured fields can be undefined and cause a crash.
- { name, description, repositoryUrl } = (await prompts(questions)) as { + { name, description, repositoryUrl } = (await prompts(questions, { + onCancel: () => { + process.exit(1); + }, + })) as {src/create/preset/preset.ts (1)
38-45
: Handle prompt cancellation to prevent runtime errors.Same issue as plugin: .trim() on possibly undefined.
- { name, description, repositoryUrl } = (await prompts(questions)) as { + { name, description, repositoryUrl } = (await prompts(questions, { + onCancel: () => { + process.exit(1); + }, + })) as {src/create/shape/shape.ts (1)
38-45
: Handle prompt cancellation to avoid .trim() on undefined.- { name, description, repositoryUrl } = (await prompts(questions)) as { + { name, description, repositoryUrl } = (await prompts(questions, { + onCancel: () => { + process.exit(1); + }, + })) as {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (11)
files/empty-project/package.json
(2 hunks)package.json
(2 hunks)src/cli.ts
(1 hunks)src/create/create.ts
(1 hunks)src/create/plugin/create-plugin.ts
(2 hunks)src/create/plugin/plugin.ts
(1 hunks)src/create/preset/create-preset.ts
(2 hunks)src/create/preset/preset.ts
(1 hunks)src/create/shape/create-shape.ts
(2 hunks)src/create/shape/shape.ts
(1 hunks)src/utils/template-utils.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- files/empty-project/package.json
🔇 Additional comments (12)
package.json (2)
5-5
: ESM switch acknowledged."type": "module" aligns with the repo-wide migration. Nothing to fix here.
25-25
: Vitest migration looks good."test": "vitest run" is correct for CI usage.
src/cli.ts (2)
2-3
: ESM import paths (.js) look good.
4-4
: Replace direct JSON import with createRequire in ESM-import pkgInfo from "../package.json"; +import { createRequire } from "node:module"; +const require = createRequire(import.meta.url); +const pkgInfo = require("../package.json");package.json is always included in the published tarball; no changes needed to “files”.
src/utils/template-utils.ts (1)
5-5
: ESM import path fix (.js) looks good.src/create/plugin/create-plugin.ts (1)
1-1
: ESM import path updates look good.Also applies to: 10-10, 13-13
src/create/shape/create-shape.ts (2)
1-1
: ESM import extension alignment looks good.The .js specifiers are correct for Node ESM and tsconfig nodenext/bundler.
Also applies to: 10-10, 13-13
150-152
: Confirm webpack entry function name.Index tokenization creates load${CapitalizedName}Shape but webpack is fed "loadParticlesShape". Likely mismatch.
If intended to point to the specific loader, update:
-async function updateShapeWebpackFile(destPath: string, name: string, description: string): Promise<void> { - await updateWebpackFile(destPath, camelize(capitalize(name, "-", " ")), description, "loadParticlesShape"); -} +async function updateShapeWebpackFile(destPath: string, name: string, description: string): Promise<void> { + const capitalizedName = capitalize(name, "-", " "); + await updateWebpackFile(destPath, camelize(capitalizedName), description, `load${capitalizedName}Shape`); +}src/create/plugin/plugin.ts (1)
1-1
: ESM import extension updates: OK.Also applies to: 4-4, 5-5
src/create/create.ts (1)
2-4
: .js imports for ESM resolution look correct.src/create/preset/preset.ts (1)
1-1
: ESM import extension updates: OK.Also applies to: 4-4, 5-5
src/create/shape/shape.ts (1)
1-1
: ESM import extension updates: OK.Also applies to: 4-4, 5-5
package.json
Outdated
"@tsparticles/eslint-config": "^3.0.3", | ||
"@tsparticles/prettier-config": "^3.0.1", | ||
"@tsparticles/tsconfig": "^3.0.1", | ||
"@tsparticles/webpack-plugin": "^3.0.3", | ||
"commander": "^14.0.0", | ||
"eslint": "^9.34.0", | ||
"eslint-config-prettier": "^10.1.8", | ||
"eslint-plugin-jsdoc": "^54.1.1", | ||
"eslint-plugin-prettier": "^5.5.4", | ||
"eslint-plugin-tsdoc": "^0.4.0", | ||
"fs-extra": "^11.3.1", | ||
"klaw": "^4.1.0", | ||
"lookpath": "^1.2.2", | ||
"madge": "^6.1.0", | ||
"path-scurry": "^1.10.1", | ||
"prettier": "^3.2.5", | ||
"prettier-plugin-multiline-arrays": "^3.0.4", | ||
"lookpath": "^1.2.3", | ||
"madge": "^8.0.0", | ||
"path-scurry": "^2.0.0", | ||
"prettier": "^3.6.2", | ||
"prettier-plugin-multiline-arrays": "^4.0.3", | ||
"prompts": "^2.4.2", | ||
"rimraf": "^5.0.5", | ||
"typescript": "^5.4.2", | ||
"webpack": "^5.90.3" | ||
"rimraf": "^6.0.1", | ||
"typescript": "^5.9.2", | ||
"typescript-eslint": "^8.41.0", | ||
"webpack": "^5.101.3" | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Dev-only tools are in dependencies; move them to devDependencies.
Shipping eslint/prettier/webpack/typescript/madge/etc. as runtime deps bloats consumers and slows installs.
Apply this diff (keep only runtime deps in "dependencies"):
"dependencies": {
- "@tsparticles/eslint-config": "^3.0.3",
- "@tsparticles/prettier-config": "^3.0.1",
- "@tsparticles/tsconfig": "^3.0.1",
- "@tsparticles/webpack-plugin": "^3.0.3",
"commander": "^14.0.0",
- "eslint": "^9.34.0",
- "eslint-config-prettier": "^10.1.8",
- "eslint-plugin-jsdoc": "^54.1.1",
- "eslint-plugin-prettier": "^5.5.4",
- "eslint-plugin-tsdoc": "^0.4.0",
"fs-extra": "^11.3.1",
"klaw": "^4.1.0",
"lookpath": "^1.2.3",
- "madge": "^8.0.0",
- "path-scurry": "^2.0.0",
- "prettier": "^3.6.2",
- "prettier-plugin-multiline-arrays": "^4.0.3",
"prompts": "^2.4.2",
- "rimraf": "^6.0.1",
- "typescript": "^5.9.2",
- "typescript-eslint": "^8.41.0",
- "webpack": "^5.101.3"
+ "path-scurry": "^2.0.0"
},
"devDependencies": {
+ "@tsparticles/eslint-config": "^3.0.3",
+ "@tsparticles/prettier-config": "^3.0.1",
+ "@tsparticles/tsconfig": "^3.0.1",
+ "@tsparticles/webpack-plugin": "^3.0.3",
+ "eslint": "^9.34.0",
+ "eslint-config-prettier": "^10.1.8",
+ "eslint-plugin-jsdoc": "^54.1.1",
+ "eslint-plugin-prettier": "^5.5.4",
+ "eslint-plugin-tsdoc": "^0.4.0",
+ "madge": "^8.0.0",
+ "prettier": "^3.6.2",
+ "prettier-plugin-multiline-arrays": "^4.0.3",
+ "rimraf": "^6.0.1",
+ "typescript": "^5.9.2",
+ "typescript-eslint": "^8.41.0",
+ "webpack": "^5.101.3"
}
Run to confirm none of these are imported at runtime:
🏁 Script executed:
#!/bin/bash
# Verify runtime imports under src/
rg -nP --type=ts -C1 '\b(madge|eslint|prettier|webpack|@tsparticles/webpack-plugin|rimraf|typescript|typescript-eslint)\b' src || true
Length of output: 9260
Move build & lint tooling to devDependencies
Shift ESLint, Prettier, Webpack, TypeScript, Madge, Rimraf and all @tsparticles/* configs/plugins into devDependencies; keep only CLI/runtime packages (commander, fs-extra, klaw, lookpath, prompts and path-scurry) under dependencies.
🤖 Prompt for AI Agents
In package.json around lines 33 to 55, several build and lint tools are
incorrectly listed under "dependencies"; move ESLint, Prettier, Webpack,
TypeScript, Madge, Rimraf and all @tsparticles/* packages and related tooling
(eslint, eslint-config-prettier, eslint-plugin-*,
prettier-plugin-multiline-arrays, typescript-eslint, webpack, madge, rimraf,
@tsparticles/eslint-config, @tsparticles/prettier-config, @tsparticles/tsconfig,
@tsparticles/webpack-plugin, prettier, typescript) into "devDependencies" and
leave only runtime/CLI packages (commander, fs-extra, klaw, lookpath, prompts,
path-scurry) under "dependencies"; update package.json accordingly and run the
package manager to reinstall or move these packages so lockfile and node_modules
reflect the change.
}, | ||
"description": "tsParticles CLI", | ||
"main": ".eslintrc.js", | ||
"main": "eslint.config.js", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
"main" points to eslint.config.js; set it to the CLI entry or drop it.
Current value breaks consumers importing the package.
- "main": "eslint.config.js",
+ "main": "dist/cli.js",
+ "exports": {
+ ".": "./dist/cli.js",
+ "./package.json": "./package.json"
+ },
📝 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.
"main": "eslint.config.js", | |
// before (around line 79) | |
- "main": "eslint.config.js", | |
// after | |
"main": "dist/cli.js", | |
"exports": { | |
".": "./dist/cli.js", | |
"./package.json": "./package.json" | |
}, |
🤖 Prompt for AI Agents
In package.json around line 79, the "main" field incorrectly points to
eslint.config.js which breaks consumers; update the "main" field to point to
your package's actual runtime/CLI entry (e.g., the built CommonJS entry like
"lib/index.js" or the published CLI entry), or remove the "main" field entirely
if the package is only a CLI and you expose binaries via "bin"; ensure the
chosen path exists in the published package and matches your build/output.
import fs from "fs-extra"; | ||
import path from "path"; | ||
import { replaceTokensInFile } from "../../utils/file-utils"; | ||
import { replaceTokensInFile } from "../../utils/file-utils.js"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__dirname is undefined in ESM; compute it from import.meta.url.
import fs from "fs-extra";
import path from "path";
+import { fileURLToPath } from "node:url";
+const DIRNAME = path.dirname(fileURLToPath(import.meta.url));
@@
- const sourcePath = path.resolve(__dirname, "..", "..", "..", "files", "create-plugin");
+ const sourcePath = path.resolve(DIRNAME, "..", "..", "..", "files", "create-plugin");
Also applies to: 168-168
🤖 Prompt for AI Agents
In src/create/plugin/create-plugin.ts around lines 11-13 (and also at line 168),
__dirname is undefined because the file is running as an ESM module; import and
use fileURLToPath from 'url' and compute __filename and __dirname from
import.meta.url, then replace uses of __dirname with that computed value.
Specifically, add an import for fileURLToPath, compute const __filename =
fileURLToPath(import.meta.url) and const __dirname = path.dirname(__filename),
and update the code at the referenced locations to use this __dirname instead of
the undefined global.
import fs from "fs-extra"; | ||
import path from "path"; | ||
import { replaceTokensInFile } from "../../utils/file-utils"; | ||
import { replaceTokensInFile } from "../../utils/file-utils.js"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__dirname is undefined in ESM; compute it from import.meta.url.
import fs from "fs-extra";
import path from "path";
+import { fileURLToPath } from "node:url";
+const DIRNAME = path.dirname(fileURLToPath(import.meta.url));
@@
- const sourcePath = path.resolve(__dirname, "..", "..", "..", "files", "create-preset");
+ const sourcePath = path.resolve(DIRNAME, "..", "..", "..", "files", "create-preset");
Also applies to: 186-186
🤖 Prompt for AI Agents
In src/create/preset/create-preset.ts around lines 11-13 (and also at line 186),
__dirname is undefined under ESM so compute it from import.meta.url instead of
relying on __dirname; add a const __filename = new URL(import.meta.url).pathname
(or use fileURLToPath(import.meta.url)) and const __dirname =
path.dirname(__filename) at the top of the module, then replace any use of the
undefined __dirname with this computed __dirname (and ensure to import
fileURLToPath from 'url' if you choose that helper).
@@ -2,7 +2,7 @@ import { exec } from "child_process"; | |||
import fs from "fs-extra"; | |||
import { lookpath } from "lookpath"; | |||
import path from "path"; | |||
import { replaceTokensInFile } from "./file-utils"; | |||
import { replaceTokensInFile } from "./file-utils.js"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__dirname usage breaks under ESM; compute from import.meta.url.
import path from "path";
+import { fileURLToPath } from "node:url";
import { replaceTokensInFile } from "./file-utils.js";
+const DIRNAME = path.dirname(fileURLToPath(import.meta.url));
@@
- await fs.copy(path.resolve(__dirname, "..", "..", "files", "empty-project"), destPath, {
+ await fs.copy(path.resolve(DIRNAME, "..", "..", "files", "empty-project"), destPath, {
Also applies to: 135-140
🤖 Prompt for AI Agents
In src/utils/template-utils.ts around line 5 (and also lines 135-140), the code
uses __dirname which breaks under ESM; replace usage by deriving the directory
from import.meta.url: import fileURLToPath from 'url' and use
fileURLToPath(import.meta.url) with path.dirname to produce a __dirname-like
variable, update any path.join calls to use that computed directory, and ensure
the top of the file imports the needed functions from 'url' and 'path' so all
file path computations work under ESM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (3)
package.json (3)
32-55
: Move dev-only tooling out of runtime dependencies.ESLint/Prettier/TypeScript/Webpack/Madge/etc. shouldn’t be in "dependencies" for a CLI; they bloat installs. Shift them to devDependencies (only keep commander, fs-extra, klaw, lookpath, prompts, path-scurry at runtime).
Run to confirm none are imported at runtime:
#!/bin/bash # Search for runtime imports of tooling libs under src/ rg -nP --type=ts -C2 '\b(eslint|prettier|webpack|@tsparticles/webpack-plugin|rimraf|typescript|typescript-eslint|madge|@tsparticles/(eslint-config|prettier-config|tsconfig))\b' src || true
57-59
: Remove self-dependency from devDependencies.The package lists itself (
@tsparticles/cli
) as a devDependency; this can cause resolution issues.Apply:
"devDependencies": { - "@babel/core": "^7.28.3", - "@tsparticles/cli": "^2.3.3", + "@babel/core": "^7.28.3",
79-80
: "main" points to eslint.config.js; set it to the CLI entry or drop it.Since "bin" already points to dist/cli.js, either remove "main" or point it to the runtime entry and add exports.
Apply:
- "main": "eslint.config.js", + "main": "dist/cli.js", + "exports": { + ".": "./dist/cli.js", + "./package.json": "./package.json" + },
🧹 Nitpick comments (1)
package.json (1)
23-25
: Script name is misleading for ESM output."build:ts:cjs" suggests CJS, but the repo is ESM ("type": "module"). Ensure tsc emits ESM (module: NodeNext/ESNext) and consider renaming to build:ts:esm.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (5)
files/empty-project/package.json
(2 hunks)files/empty-project/webpack.config.js
(1 hunks)package.json
(2 hunks)src/cli.ts
(1 hunks)src/tsconfig.json
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/tsconfig.json
🧰 Additional context used
🧬 Code graph analysis (1)
files/empty-project/webpack.config.js (1)
eslint.config.js (1)
__dirname
(3-3)
🪛 Biome (2.1.2)
files/empty-project/webpack.config.js
[error] 2-2: Expected a semicolon or an implicit semicolon after a statement, but found none
An explicit or implicit semicolon is expected here...
...Which is required to end this statement
(parse)
[error] 2-2: Expected a semicolon or an implicit semicolon after a statement, but found none
An explicit or implicit semicolon is expected here...
...Which is required to end this statement
(parse)
src/cli.ts
[error] 4-4: Expected a semicolon or an implicit semicolon after a statement, but found none
An explicit or implicit semicolon is expected here...
...Which is required to end this statement
(parse)
[error] 4-4: Expected a semicolon or an implicit semicolon after a statement, but found none
An explicit or implicit semicolon is expected here...
...Which is required to end this statement
(parse)
🔇 Additional comments (1)
package.json (1)
3-3
: Version vs PR title mismatch.PR title says 3.0.0 but package.json is 3.0.5. Confirm intended semver before release/tagging.
@@ -2,6 +2,7 @@ | |||
"name": "@tsparticles/empty-template", | |||
"version": "1.0.0", | |||
"private": true, | |||
"type": "module", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"type": "module" conflicts with CommonJS "main" entry; use exports map or .cjs.
With "type": "module", Node treats .js as ESM. Pointing "main" at dist/cjs/index.js (CommonJS) will break require/import. Prefer an exports map and emit .cjs for CJS output.
Apply:
- "main": "dist/cjs/index.js",
- "module": "dist/esm/index.js",
- "types": "dist/types/index.d.ts",
+ "exports": {
+ ".": {
+ "types": "./dist/types/index.d.ts",
+ "import": "./dist/esm/index.js",
+ "require": "./dist/cjs/index.cjs"
+ },
+ "./package.json": "./package.json"
+ },
+ "main": "./dist/esm/index.js",
+ "types": "./dist/types/index.d.ts",
Note: ensure your build emits dist/cjs/index.cjs (or rename accordingly).
Also applies to: 81-83
🤖 Prompt for AI Agents
In files/empty-project/package.json around line 5, "type": "module" conflicts
with the CommonJS "main" entry; update package.json and build outputs so ESM and
CJS artifacts match: either remove "type": "module" to keep CommonJS, or
(preferred) keep "type": "module", change "main" to point to a CJS file like
"dist/cjs/index.cjs", add an "exports" map exposing "./" with import ->
"dist/esm/index.js" and require -> "dist/cjs/index.cjs", and ensure the build
emits dist/cjs/index.cjs (or rename your CJS output) so require consumers and
import consumers both resolve correctly.
src/cli.ts
Outdated
import { buildCommand } from "./build/build.js"; | ||
import { createCommand } from "./create/create.js"; | ||
import pkgInfo from "../package.json" assert { type: "json" }; | ||
import { program } from "commander"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Use import attributes with JSON default import (ESM).
Replace assert { type: "json" }
with with { type: "json" }
to satisfy modern Node and parsers. No other changes needed.
Apply:
-import pkgInfo from "../package.json" assert { type: "json" };
+import pkgInfo from "../package.json" with { type: "json" };
📝 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.
import { buildCommand } from "./build/build.js"; | |
import { createCommand } from "./create/create.js"; | |
import pkgInfo from "../package.json" assert { type: "json" }; | |
import { program } from "commander"; | |
import { buildCommand } from "./build/build.js"; | |
import { createCommand } from "./create/create.js"; | |
import pkgInfo from "../package.json" with { type: "json" }; | |
import { program } from "commander"; |
🧰 Tools
🪛 Biome (2.1.2)
[error] 4-4: Expected a semicolon or an implicit semicolon after a statement, but found none
An explicit or implicit semicolon is expected here...
...Which is required to end this statement
(parse)
[error] 4-4: Expected a semicolon or an implicit semicolon after a statement, but found none
An explicit or implicit semicolon is expected here...
...Which is required to end this statement
(parse)
🤖 Prompt for AI Agents
In src/cli.ts around lines 2 to 5, the JSON import uses the old attribute syntax
`assert { type: "json" }`; update that import to use the modern attribute
keyword by replacing `assert { type: "json" }` with `with { type: "json" }` so
the package.json import reads using `with { type: "json" }`; keep the rest of
the line unchanged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
package.json (1)
1-12
: Enforce Node 22 at install time.Add engines to prevent unsupported runtime usage.
"engines": { "node": ">=22" }
♻️ Duplicate comments (4)
package.json (4)
57-59
: Remove self-dependency.Listing @tsparticles/cli as a devDependency can break resolution.
"devDependencies": { "@babel/core": "^7.28.3", - "@tsparticles/cli": "^2.3.3",
79-79
: "main" should not point to eslint.config.js.Either drop main (CLI-only) or point it to the runtime entry and add exports.
- "main": "eslint.config.js", + "main": "dist/cli.js", + "exports": { + ".": "./dist/cli.js", + "./package.json": "./package.json" + },If this package isn’t meant to be imported, remove main entirely instead.
56-77
: Add the build/lint tooling under devDependencies.Completes the move started above.
"devDependencies": { "@babel/core": "^7.28.3", + "@tsparticles/eslint-config": "^3.0.5", + "@tsparticles/prettier-config": "^3.0.1", + "@tsparticles/tsconfig": "^3.0.5", + "@tsparticles/webpack-plugin": "^3.0.5", + "eslint": "^9.34.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-jsdoc": "^54.1.1", + "eslint-plugin-prettier": "^5.5.4", + "eslint-plugin-tsdoc": "^0.4.0", + "madge": "^8.0.0", + "prettier": "^3.6.2", + "prettier-plugin-multiline-arrays": "^4.0.3", + "rimraf": "^6.0.1", + "typescript": "^5.9.2", + "typescript-eslint": "^8.41.0", + "webpack": "^5.101.3", "@tsparticles/engine": "^3.9.1", "@types/eslint": "^9.6.1", "@types/estree": "^1.0.8", "@types/fs-extra": "^11.0.4", "@types/klaw": "^3.0.7", "@types/madge": "^5.0.3", "@types/node": "^24.3.0", "@types/prompts": "^2.4.9", "@types/webpack-env": "^1.18.8", "babel-loader": "^10.0.0", "browserslist": "^4.25.4", "copyfiles": "^2.4.1", "cross-env": "^10.0.0", "terser-webpack-plugin": "^5.3.14", "ts-node": "^10.9.2", "vitest": "^1.5.6", "webpack-bundle-analyzer": "^4.10.2", "webpack-cli": "^6.0.1" },
33-55
: Move build/lint tooling to devDependencies.These inflate consumer installs and aren’t needed at runtime.
"dependencies": { - "@tsparticles/eslint-config": "^3.0.5", - "@tsparticles/prettier-config": "^3.0.1", - "@tsparticles/tsconfig": "^3.0.5", - "@tsparticles/webpack-plugin": "^3.0.5", "commander": "^14.0.0", - "eslint": "^9.34.0", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-jsdoc": "^54.1.1", - "eslint-plugin-prettier": "^5.5.4", - "eslint-plugin-tsdoc": "^0.4.0", "fs-extra": "^11.3.1", "klaw": "^4.1.0", "lookpath": "^1.2.3", - "madge": "^8.0.0", "path-scurry": "^2.0.0", - "prettier": "^3.6.2", - "prettier-plugin-multiline-arrays": "^4.0.3", "prompts": "^2.4.2", - "rimraf": "^6.0.1", - "typescript": "^5.9.2", - "typescript-eslint": "^8.41.0", - "webpack": "^5.101.3" + "prompts": "^2.4.2" },Add the removed packages under devDependencies (see next comment).
🧹 Nitpick comments (7)
src/tsconfig.json (2)
16-28
: Trim redundant libs; use a single, stable ES lib.Including ESNext plus every yearly lib is redundant and can mask breaking lib changes. Prefer a single target (e.g., ES2024) that matches Node 22.
- "lib": [ - "ESNext", - "ES2024", - "ES2023", - "ES2022", - "ES2021", - "ES2020", - "ES2019", - "ES2018", - "ES2017", - "ES2016", - "ES2015" - ], + "lib": ["ES2024"],
90-97
: Consider verbatimModuleSyntax for cleaner ESM/CJS interop.Helps prevent accidental synthetic default imports and matches TS 5+ guidance when using NodeNext.
/* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + "verbatimModuleSyntax": true,eslint.config.js (1)
1-2
: Use node: specifiers for built-ins.Minor, but recommended for clarity in ESM.
-import path from "path"; -import {fileURLToPath} from "url"; +import path from "node:path"; +import {fileURLToPath} from "node:url";files/empty-project/webpack.config.js (3)
2-4
: Prefer node: specifiers for built-ins.Keeps ESM imports explicit.
-import {fileURLToPath} from "url"; +import {fileURLToPath} from "node:url"; @@ -import path from "path"; +import path from "node:path";
6-9
: Avoid FS + top-level await; import JSON directly.Use ESM JSON import to simplify and remove runtime I/O.
-import fs from "fs-extra"; @@ -const __filename = fileURLToPath(import.meta.url), - __dirname = path.dirname(__filename), - rootPkgPath = path.join(__dirname, "package.json"), - pkg = await fs.readJson(rootPkgPath); +import pkg from "./package.json" with { type: "json" }; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename);
8-8
: Variable naming nit.If you keep the fs approach, rootPkgPath isn’t “root”; consider pkgPath.
- rootPkgPath = path.join(__dirname, "package.json"), + pkgPath = path.join(__dirname, "package.json"),package.json (1)
1-12
: Optional: Limit published contents.Add files to ship only dist and docs.
"files": [ "dist", "README.md", "LICENSE" ]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (7)
eslint.config.js
(1 hunks)files/empty-project/package.json
(2 hunks)files/empty-project/webpack.config.js
(1 hunks)package.json
(2 hunks)src/cli.ts
(1 hunks)src/tsconfig.json
(2 hunks)tests/tsconfig.json
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- src/cli.ts
- files/empty-project/package.json
- tests/tsconfig.json
🧰 Additional context used
🧬 Code graph analysis (1)
files/empty-project/webpack.config.js (2)
eslint.config.js (1)
__dirname
(6-6)scripts/postversion.js (1)
pkg
(13-14)
🔇 Additional comments (3)
src/tsconfig.json (2)
42-47
: NodeNext module + resolution: LGTM.This aligns with ESM and explicit .js specifiers across the repo.
126-127
: Project references present but not built; verify intent.You compile with “tsc -p src” (not build mode). If references are needed, switch to “tsc -b” and mark the referenced project as composite; otherwise, remove the references to reduce confusion.
eslint.config.js (1)
8-22
: Flat config structure otherwise looks good.Type-aware linting via project tsconfig and merging the shared config is solid.
eslint.config.js
Outdated
languageOptions: { | ||
parserOptions: { | ||
project: [path.resolve(__dirname, "src/tsconfig.json")], | ||
tsconfigRootDir: __dirname, | ||
sourceType: "module" | ||
} | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix sourceType placement (ESLint flat config).
sourceType belongs under languageOptions, not parserOptions. Current placement may be ignored.
- languageOptions: {
- parserOptions: {
- project: [path.resolve(__dirname, "src/tsconfig.json")],
- tsconfigRootDir: __dirname,
- sourceType: "module"
- }
- },
+ languageOptions: {
+ sourceType: "module",
+ parserOptions: {
+ project: [path.resolve(__dirname, "src/tsconfig.json")],
+ tsconfigRootDir: __dirname
+ }
+ },
📝 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.
languageOptions: { | |
parserOptions: { | |
project: [path.resolve(__dirname, "src/tsconfig.json")], | |
tsconfigRootDir: __dirname, | |
sourceType: "module" | |
} | |
}, | |
languageOptions: { | |
sourceType: "module", | |
parserOptions: { | |
project: [path.resolve(__dirname, "src/tsconfig.json")], | |
tsconfigRootDir: __dirname | |
} | |
}, |
🤖 Prompt for AI Agents
In eslint.config.js around lines 11 to 17, the sourceType is incorrectly placed
inside parserOptions (so it may be ignored by ESLint flat config); move
sourceType out of parserOptions and set it directly on languageOptions (e.g.,
languageOptions.sourceType = "module"), leaving parserOptions with only project
and tsconfigRootDir, so ESLint recognizes the module source type correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
package.json (1)
21-31
: CI failure: package.json is missing in dist; include it or update the code
Reading package.json in src/build/build-prettier.ts (line 66) breaks at runtime because dist/ lacks package.json. Either:
- copy package.json into dist in your build scripts (e.g. add
"copy:pkg": "copyfiles -u 0 package.json dist"
and invoke it inbuild
/build:ci
), or- stop hardcoding a JSON read from CWD—import it as a JSON module (enable
resolveJsonModule
in tsconfig and useimport pkg from '../package.json' with { type: 'json' }
) or resolve the project root viapath.resolve(__dirname, '..', '..', 'package.json')
.
♻️ Duplicate comments (4)
files/empty-project/package.json (1)
5-5
: "type": "module" + CommonJS main/module combo will confuse consumers; add an exports map and emit .cjs for CJS.With ESM type, .js is ESM. Pointing "main" to a CommonJS path without .cjs and using a separate "module" field is fragile. Prefer an explicit exports map and a .cjs filename for CJS output.
"license": "MIT", "bugs": { "url": "https://github.com/tsparticles/empty-template/issues" }, - "main": "dist/cjs/index.js", - "module": "dist/esm/index.js", - "types": "dist/types/index.d.ts", + "exports": { + ".": { + "types": "./dist/types/index.d.ts", + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.cjs" + }, + "./package.json": "./package.json" + }, + "main": "./dist/esm/index.js", + "types": "./dist/types/index.d.ts",Ensure the build emits dist/cjs/index.cjs (or rename accordingly).
Also applies to: 81-83
package.json (3)
32-55
: Move build/lint tooling out of runtime dependencies.eslint/prettier/webpack/ts/madge/rimraf and internal configs should be devDependencies to avoid bloating consumers.
"dependencies": { - "@tsparticles/eslint-config": "^3.0.5", - "@tsparticles/prettier-config": "^3.0.1", - "@tsparticles/tsconfig": "^3.0.5", - "@tsparticles/webpack-plugin": "^3.0.5", "commander": "^14.0.0", - "eslint": "^9.34.0", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-jsdoc": "^54.1.1", - "eslint-plugin-prettier": "^5.5.4", - "eslint-plugin-tsdoc": "^0.4.0", "fs-extra": "^11.3.1", "klaw": "^4.1.0", "lookpath": "^1.2.3", - "madge": "^8.0.0", "path-scurry": "^2.0.0", - "prettier": "^3.6.2", - "prettier-plugin-multiline-arrays": "^4.0.3", "prompts": "^2.4.2", - "rimraf": "^6.0.1", - "typescript": "^5.9.2", - "typescript-eslint": "^8.41.0", - "webpack": "^5.101.3" + "@tsparticles/eslint-config": "^3.0.5", + "@tsparticles/prettier-config": "^3.0.1", + "@tsparticles/tsconfig": "^3.0.5", + "@tsparticles/webpack-plugin": "^3.0.5", + "eslint": "^9.34.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-jsdoc": "^54.1.1", + "eslint-plugin-prettier": "^5.5.4", + "eslint-plugin-tsdoc": "^0.4.0", + "madge": "^8.0.0", + "prettier": "^3.6.2", + "prettier-plugin-multiline-arrays": "^4.0.3", + "rimraf": "^6.0.1", + "typescript": "^5.9.2", + "typescript-eslint": "^8.41.0", + "webpack": "^5.101.3" },
57-59
: Remove self-dependency from devDependencies.Listing @tsparticles/cli as a devDependency of itself can cause resolver loops.
"devDependencies": { "@babel/core": "^7.28.3", - "@tsparticles/cli": "^2.3.3", "@tsparticles/engine": "^3.9.1",
79-79
: "main" should not point to eslint.config.js; set to the runtime entry or drop it.This breaks consumers importing the package. For a CLI, either point main to the built CLI or omit it and rely on "bin".
- "main": "eslint.config.js", + "main": "dist/cli.js", + "exports": { + ".": "./dist/cli.js", + "./package.json": "./package.json" + },
🧹 Nitpick comments (2)
package.json (2)
5-8
: Declare engines to reflect Node 22 requirement.Helps users and CI fail fast on unsupported Node versions.
"type": "module", + "engines": { + "node": ">=22" + }, "bin": {
21-31
: Minor: script name “build:ts:cjs” is misleading under ESM.If output is ESM, rename to “build:ts” to avoid confusion.
- "build:ts": "pnpm run build:ts:cjs", - "build:ts:cjs": "tsc -p src", + "build:ts": "tsc -p src",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
files/empty-project/package.json
(2 hunks)package.json
(2 hunks)src/build/build.ts
(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/build/build.ts
🧰 Additional context used
🪛 GitHub Actions: Node.js CI
package.json
[error] 1-1: npm run build failed. ENOENT: no such file or directory, open '/home/runner/work/cli/cli/tests/tmp-files/foo-preset/node_modules/@tsparticles/cli/dist/package.json'.
[error] 1-1: npm run build failed. ENOENT: no such file or directory, open '/home/runner/work/cli/cli/tests/tmp-files/bar-preset/node_modules/@tsparticles/cli/dist/package.json'.
[error] 1-1: npm run build failed. ENOENT: no such file or directory, open '/home/runner/work/cli/cli/tests/tmp-files/foo-plugin/node_modules/@tsparticles/cli/dist/package.json'.
[error] 1-1: npm run build failed. ENOENT: no such file or directory, open '/home/runner/work/cli/cli/tests/tmp-files/bar-plugin/node_modules/@tsparticles/cli/dist/package.json'.
[error] 1-1: npm run build failed. ENOENT: no such file or directory, open '/home/runner/work/cli/cli/tests/tmp-files/foo-shape/node_modules/@tsparticles/cli/dist/package.json'.
[error] 1-1: npm run build failed. ENOENT: no such file or directory, open '/home/runner/work/cli/cli/tests/tmp-files/bar-shape/node_modules/@tsparticles/cli/dist/package.json'.
🔇 Additional comments (1)
files/empty-project/package.json (1)
86-106
: Verify ESM build settings
- Ensure your
files/empty-project/tsconfig.json
(which extends@tsparticles/tsconfig/tsconfig.json
) ultimately sets
compilerOptions.module = "NodeNext"
(or"ESNext"
) and
compilerOptions.target ≥ "ES2022"
.- Confirm the
@tsparticles/tsconfig
shared config (and its base) include those values or override them locally.- Verify that
loadParticlesTemplate
(in your webpack config) emits ESM output (not CJS) and that no Babel config is falling back to CJS.
There was a problem hiding this 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
src/build/build-diststats.ts (1)
60-68
: Fix JSON import and path normalization; replace deprecated exists and ensure absolute compareDynamic-importing package.json without assertions will throw in Node 22 ESM, and mixing relative/absolute paths can break the bundleSize match. Also prefer fs.pathExists over fs.exists.
Apply this patch to robustly read JSON, normalize paths to absolute, and avoid deprecated exists:
- const path = await import("path"), - distFolder = path.join(basePath, "dist"), - pkgInfo = (await fs.exists(path.join(distFolder, "package.json"))) - ? ((await import(path.join(distFolder, "package.json"))) as { jsdelivr?: string }) - : {}, - bundlePath = - (await fs.exists(distFolder)) && pkgInfo.jsdelivr - ? path.join(distFolder, pkgInfo.jsdelivr) - : undefined; + const path = await import("path"), + distFolder = path.resolve(basePath, "dist"), + pkgJsonPath = path.join(distFolder, "package.json"), + pkgInfo = (await fs.pathExists(pkgJsonPath)) + ? ((await fs.readJson(pkgJsonPath)) as { jsdelivr?: string }) + : {}, + bundlePath = + (await fs.pathExists(distFolder)) && pkgInfo.jsdelivr + ? path.resolve(distFolder, pkgInfo.jsdelivr) + : undefined;package.json (1)
21-25
: Rename build script suffix to reflect ESM output
Thebuild:ts:cjs
script actually emits ESM (module: "NodeNext"
) intodist
, so the:cjs
suffix is misleading—rename it to something likebuild:ts:esm
.src/utils/file-utils.ts (1)
26-33
: RegExp handling breaks flags and treats string patterns as regex.new RegExp(token.from, "g") drops original flags (e.g., i) and makes plain strings act as regex. Preserve flags and escape strings.
- for (const token of item.tokens) { - const regex = new RegExp(token.from, "g"); - - data = data.replace(regex, token.to); - } + for (const token of item.tokens) { + const regex = + typeof token.from === "string" + ? new RegExp(token.from.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g") + : new RegExp( + token.from.source, + token.from.flags.includes("g") ? token.from.flags : token.from.flags + "g", + ); + data = data.replace(regex, token.to); + }src/create/shape/create-shape.ts (1)
129-136
: Bug: missing closing quote in README token replacementThe replacement for
shape.type
loses the closing quote.- to: `shape.type: "${camelizedName}`, + to: `shape.type: "${camelizedName}"`,
♻️ Duplicate comments (4)
src/create/plugin/create-plugin.ts (1)
168-168
: __dirname is undefined in ESM — compute it from import.meta.url.This will throw at runtime under "type": "module".
import path from "path"; +import { fileURLToPath } from "node:url"; +const DIRNAME = path.dirname(fileURLToPath(import.meta.url)); @@ - const sourcePath = path.join(__dirname, "..", "..", "..", "files", "create-plugin"); + const sourcePath = path.join(DIRNAME, "..", "..", "..", "files", "create-plugin");package.json (3)
58-58
: Remove self-dependency from devDependencies.Listing @tsparticles/cli under its own devDependencies can cause resolver loops.
- "@tsparticles/cli": "^2.3.3",
79-79
: “main” points to eslint.config.js — this breaks consumers.Point main/exports to the runtime entry (dist/cli.js) or drop main if CLI-only.
- "main": "eslint.config.js", + "main": "dist/cli.js", + "exports": { + ".": "./dist/cli.js", + "./package.json": "./package.json" + },
33-55
: Move build/lint tooling to devDependencies to avoid bloating consumers.ESLint/Prettier/Webpack/TypeScript/Madge/Rimraf and related tsparticles configs are dev-only.
"dependencies": { - "@tsparticles/eslint-config": "^3.0.5", - "@tsparticles/prettier-config": "^3.0.1", - "@tsparticles/tsconfig": "^3.0.5", - "@tsparticles/webpack-plugin": "^3.0.5", "commander": "^14.0.0", - "eslint": "^9.34.0", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-jsdoc": "^54.1.1", - "eslint-plugin-prettier": "^5.5.4", - "eslint-plugin-tsdoc": "^0.4.0", "fs-extra": "^11.3.1", "klaw": "^4.1.0", "lookpath": "^1.2.3", - "madge": "^8.0.0", "path-scurry": "^2.0.0", - "prettier": "^3.6.2", - "prettier-plugin-multiline-arrays": "^4.0.3", "prompts": "^2.4.2", - "rimraf": "^6.0.1", - "typescript": "^5.9.2", - "typescript-eslint": "^8.41.0", - "webpack": "^5.101.3" }, "devDependencies": { + "@tsparticles/eslint-config": "^3.0.5", + "@tsparticles/prettier-config": "^3.0.1", + "@tsparticles/tsconfig": "^3.0.5", + "@tsparticles/webpack-plugin": "^3.0.5", + "eslint": "^9.34.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-jsdoc": "^54.1.1", + "eslint-plugin-prettier": "^5.5.4", + "eslint-plugin-tsdoc": "^0.4.0", + "madge": "^8.0.0", + "prettier": "^3.6.2", + "prettier-plugin-multiline-arrays": "^4.0.3", + "rimraf": "^6.0.1", + "typescript": "^5.9.2", + "typescript-eslint": "^8.41.0", + "webpack": "^5.101.3" }
🧹 Nitpick comments (13)
src/build/build-diststats.ts (1)
45-47
: Normalize compare path inside getFolderStats (optional hardening)Ensure both sides are absolute before equality to avoid false negatives when callers pass relative basePath.
TypeScript snippet (outside the changed hunk):
// before if (bundlePath && path.join(folderPath, dirent.name) === bundlePath) { stats.bundleSize += fileStats.size; } // after const fullPath = path.resolve(folderPath, dirent.name); if (bundlePath && fullPath === bundlePath) { stats.bundleSize += fileStats.size; }Optional: hoist a single top-level import for path and drop the per-function dynamic import:
import path from "node:path";tests/string-utils.test.ts (1)
6-6
: Sharpen test description wording.Prefer “returns empty string for empty input” for clarity.
- it("should successfully compare empty strings", () => { + it("returns empty string for empty input", () => {Apply similarly to the empty cases in camelize and dash.
src/create/plugin/create-plugin.ts (1)
101-109
: Handle repo URLs without “.git”.If the remote URL lacks .git, substring will misbehave. Safer to strip an optional suffix.
- repoPath = repoUrl.includes(stringSearch) - ? repoUrl.substring( - repoUrl.indexOf(trailingSlashSearch) + trailingSlashSearch.length, - repoUrl.indexOf(".git"), - ) + repoPath = repoUrl.includes(stringSearch) + ? repoUrl + .slice(repoUrl.indexOf(trailingSlashSearch) + trailingSlashSearch.length) + .replace(/\.git$/, "") : "tsparticles/plugin-template";tests/create-preset.test.ts (3)
6-6
: Stabilize heavy integration tests (serialize, timeout, guaranteed cleanup).Project generation runs installs/builds; guard with sequential suite, large timeout, and finally cleanup.
-describe("create-preset", () => { +describe.sequential("create-preset", () => { it("should have created the preset project", async () => { - const destDir = path.join(DIRNAME, "tmp-files", "foo-preset"); - - await createPresetTemplate("foo", "Foo", "", destDir); - - const pkgPath = path.join(destDir, "package.json"); - - console.log(pkgPath); - - const pkgInfo = await fs.readJSON(pkgPath); - - expect(pkgInfo.name).toBe("tsparticles-preset-foo"); - - await fs.remove(destDir); - }); + const destDir = path.join(DIRNAME, "tmp-files", "foo-preset"); + try { + await createPresetTemplate("foo", "Foo", "", destDir); + const pkgPath = path.join(destDir, "package.json"); + const pkgInfo = await fs.readJSON(pkgPath); + expect(pkgInfo.name).toBe("tsparticles-preset-foo"); + } finally { + await fs.remove(destDir); + } + }, 180000); @@ - it("should have created the preset project, w/ repo", async () => { + it("should have created the preset project, w/ repo", async () => { const destDir = path.join(DIRNAME, "tmp-files", "bar-preset"); - - await createPresetTemplate("bar", "Bar", "https://github.com/matteobruni/tsparticles", destDir); - - const pkgPath = path.join(destDir, "package.json"); - - console.log(pkgPath); - - const pkgInfo = await fs.readJSON(pkgPath); - - expect(pkgInfo.name).toBe("tsparticles-preset-bar"); - - await fs.remove(destDir); - }); + try { + await createPresetTemplate("bar", "Bar", "https://github.com/matteobruni/tsparticles", destDir); + const pkgPath = path.join(destDir, "package.json"); + const pkgInfo = await fs.readJSON(pkgPath); + expect(pkgInfo.name).toBe("tsparticles-preset-bar"); + } finally { + await fs.remove(destDir); + } + }, 180000);Also applies to: 7-21, 23-37
14-16
: Remove noisy console logging in CI.These logs add noise without aiding assertions.
- console.log(pkgPath);
Also applies to: 30-32
2-2
: Avoid network-heavy installs/builds in tests (introduce skip flag).Consider adding an options param to createPresetTemplate({ skipInstallBuild: true }) and gating runInstall/runBuild behind it; then set it in tests.
Would you like me to open a follow-up PR adding the flag across create-plugin/preset/shape and updating tests?
package.json (1)
1-31
: Declare supported Node versions.Add engines to reflect Node 22 target (and 20 LTS if supported).
"publishConfig": { "access": "public" }, + "engines": { + "node": ">=20.11 <21 || >=22" + },src/utils/file-utils.ts (1)
83-84
: Trim git config output.exec returns a string ending with a newline; trim to avoid subtle token/URL issues.
- resolve(stdout); + resolve(stdout.trim());tests/file-utils.test.ts (1)
91-95
: Stabilize repo URL test (environment-dependent)
getRepositoryUrl()
returns""
if git isn’t present or remote isn’t set, making this flaky in CI. Mocklookpath
/child_process.exec
or make the assertion conditional.Example (preferred, mock within this test block):
// at top of the file (before importing getRepositoryUrl), or use dynamic import below // vi.mock("lookpath", () => ({ lookpath: async () => "git" })); // vi.mock("node:child_process", () => ({ exec: (_: any, cb: any) => cb(null, "git@github.com:foo/bar.git\n") })); it("should return the repository url", async () => { const url = await getRepositoryUrl(); if (!url) { // skip in environments without git/remote return; } expect(url.trim()).not.toBe(""); });tests/create-shape.test.ts (4)
1-3
: Mock heavy steps to avoid timeouts; or increase per-test timeoutStub
runInstall
/runBuild
to keep tests fast and deterministic. Ensure mocks are declared before importing the SUT.-import { describe, it, expect } from "vitest"; -import { createShapeTemplate } from "../src/create/shape/create-shape.js"; +import { describe, it, expect, vi } from "vitest"; +vi.mock("../src/utils/template-utils.js", async () => { + const mod = await vi.importActual<typeof import("../src/utils/template-utils.js")>("../src/utils/template-utils.js"); + return { + ...mod, + runInstall: vi.fn(async () => {}), + runBuild: vi.fn(async () => {}), + }; +}); +import { createShapeTemplate } from "../src/create/shape/create-shape.js";If you prefer not to mock, pass a larger timeout to each
it(..., 180_000)
.
14-15
: Remove debug logging
console.log(pkgPath)
is noisy in CI.- console.log(pkgPath);
Also applies to: 30-31
7-21
: Ensure cleanup on failuresWrap body in try/finally so the temp dir is removed even if assertions fail.
- it("should have created the shape project", async () => { + it("should have created the shape project", async () => { const destDir = path.join(__dirname, "tmp-files", "foo-shape"); - - await createShapeTemplate("foo", "Foo", "", destDir); - - const pkgPath = path.join(destDir, "package.json"); - - const pkgInfo = await fs.readJSON(pkgPath); - - expect(pkgInfo.name).toBe("tsparticles-shape-foo"); - - await fs.remove(destDir); + try { + await createShapeTemplate("foo", "Foo", "", destDir); + const pkgPath = path.join(destDir, "package.json"); + const pkgInfo = await fs.readJSON(pkgPath); + expect(pkgInfo.name).toBe("tsparticles-shape-foo"); + } finally { + await fs.remove(destDir); + } - }); + });
24-37
: Mirror cleanup pattern in second testApply the same try/finally cleanup to the repo-URL variant.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (14)
eslint.config.js
(1 hunks)package.json
(2 hunks)src/build/build-diststats.ts
(1 hunks)src/create/plugin/create-plugin.ts
(5 hunks)src/create/preset/create-preset.ts
(6 hunks)src/create/shape/create-shape.ts
(5 hunks)src/utils/file-utils.ts
(3 hunks)src/utils/template-utils.ts
(5 hunks)tests/create-plugin.test.ts
(1 hunks)tests/create-preset.test.ts
(1 hunks)tests/create-shape.test.ts
(1 hunks)tests/file-utils.test.ts
(4 hunks)tests/string-utils.test.ts
(1 hunks)vitest.config.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
- tests/create-plugin.test.ts
- src/utils/template-utils.ts
- src/create/preset/create-preset.ts
- vitest.config.ts
- eslint.config.js
🧰 Additional context used
🧬 Code graph analysis (6)
tests/string-utils.test.ts (1)
src/utils/string-utils.ts (3)
capitalize
(7-18)camelize
(26-28)dash
(35-41)
tests/create-shape.test.ts (2)
eslint.config.js (1)
__dirname
(6-6)src/create/shape/create-shape.ts (1)
createShapeTemplate
(161-184)
src/create/plugin/create-plugin.ts (1)
eslint.config.js (1)
__dirname
(6-6)
tests/file-utils.test.ts (1)
src/utils/file-utils.ts (2)
getDestinationDir
(49-65)getRepositoryUrl
(70-86)
tests/create-preset.test.ts (2)
eslint.config.js (1)
__dirname
(6-6)src/create/preset/create-preset.ts (1)
createPresetTemplate
(180-204)
src/create/shape/create-shape.ts (1)
eslint.config.js (1)
__dirname
(6-6)
🪛 GitHub Actions: Node.js CI
src/build/build-diststats.ts
[error] 1-1: Prettier formatting check failed for file 'src/build/build-diststats.ts' (prettier --check ./src/*). Run 'prettier --write' to fix code style issues.
🔇 Additional comments (6)
src/build/build-diststats.ts (1)
1-1
: Fix Prettier check failureThe CI reports Prettier formatting errors for this file. Please run Prettier locally to align with the repo config.
Commands:
- pnpm: pnpm prettier --write src/build/build-diststats.ts
- npm: npx prettier --write src/build/build-diststats.ts
tests/string-utils.test.ts (1)
6-8
: Vitest structure fix looks good.Empty-string assertions are now inside it() blocks and will execute. Nice catch-up with earlier feedback.
Also applies to: 54-56, 102-104
src/utils/file-utils.ts (1)
50-65
: Path resolution change is fine.path.join(process.cwd(), destination) remains absolute; behavior matches prior resolve(). LGTM.
src/create/shape/create-shape.ts (2)
150-152
: Verify exported loader name used in webpack config
updateShapeWebpackFile
hardcodes"loadParticlesShape"
, while other replacements useload${Capitalized}Shape
. Confirm the template expects the generic name; otherwise pass the specific one to avoid broken UMD/webpack entry.
1-1
: ESM import paths and path.join usage look goodExplicit
.js
extensions andpath.join
-based paths align with the repo’s ESM strategy.Also applies to: 10-10, 13-13, 25-26, 110-110
tests/file-utils.test.ts (1)
97-99
: Teardown LGTM
afterAll
cleanup is correct.
import path from "path"; | ||
import { replaceTokensInFile } from "../../utils/file-utils"; | ||
import { replaceTokensInFile } from "../../utils/file-utils.js"; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix: __dirname is undefined under ESM
__dirname
isn’t available in ESM. Compute it from import.meta.url
before using it at Line 167.
Apply:
import fs from "fs-extra";
import path from "path";
+import { fileURLToPath } from "node:url";
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
- const sourcePath = path.join(__dirname, "..", "..", "..", "files", "create-shape");
+ const sourcePath = path.join(__dirname, "..", "..", "..", "files", "create-shape");
Also applies to: 167-168
🤖 Prompt for AI Agents
In src/create/shape/create-shape.ts around lines 12 and 167-168, __dirname is
used but undefined under ESM; add a computed dirname using import.meta.url and
fileURLToPath before its use: import fileURLToPath from 'url' (or add a named
import) at the top and compute const __dirname =
path.dirname(fileURLToPath(import.meta.url)); then replace the direct __dirname
usage at lines 167-168 with the computed value; ensure the new import is added
alongside existing imports and no CommonJS globals remain.
import { describe, it } from "mocha"; | ||
import { expect } from "chai"; | ||
import { createPresetTemplate } from "../src/create/preset/create-preset"; | ||
import { describe, it, expect } from "vitest"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Audit other ESM files for __dirname usage.
create-preset implementation still uses __dirname per repo snippet; scan and fix globally.
🏁 Script executed:
#!/bin/bash
# List residual __dirname usages under src/ and tests/
rg -nP '\b__dirname\b' -g 'src/**' -g 'tests/**' -S
Length of output: 1298
Replace __dirname in all ESM modules
The following ESM files still reference __dirname
and must be converted to URL-based resolution (e.g.
import { fileURLToPath } from 'url';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
):
- src/cli.ts
- src/utils/template-utils.ts
- src/create/shape/create-shape.ts
- src/create/preset/create-preset.ts
- src/create/plugin/create-plugin.ts
- tests/create-shape.test.ts
- tests/create-preset.test.ts
- tests/create-plugin.test.ts
🤖 Prompt for AI Agents
In tests/create-preset.test.ts around line 1 (and the other listed ESM files:
src/cli.ts, src/utils/template-utils.ts, src/create/shape/create-shape.ts,
src/create/preset/create-preset.ts, src/create/plugin/create-plugin.ts,
tests/create-shape.test.ts, tests/create-plugin.test.ts), remove any use of the
CommonJS __dirname and instead derive the directory via the URL API: import
fileURLToPath from 'url' and compute __dirname with fileURLToPath(new URL('.',
import.meta.url)); update all path resolutions that relied on __dirname to use
this computed value (or use new URL(..., import.meta.url) directly) so the
modules remain valid ESM.
it("should have created the preset project", async () => { | ||
const destDir = path.resolve(path.join(__dirname, "tmp-files", "foo-preset")); | ||
const destDir = path.join(__dirname, "tmp-files", "foo-preset"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__dirname is undefined in ESM tests — derive it via import.meta.url.
Without this, path.join will fail under Vitest ESM.
import path from "path";
+import { fileURLToPath } from "node:url";
+const DIRNAME = path.dirname(fileURLToPath(import.meta.url));
@@
- const destDir = path.join(__dirname, "tmp-files", "foo-preset");
+ const destDir = path.join(DIRNAME, "tmp-files", "foo-preset");
@@
- const destDir = path.join(__dirname, "tmp-files", "bar-preset");
+ const destDir = path.join(DIRNAME, "tmp-files", "bar-preset");
Also applies to: 24-24
🤖 Prompt for AI Agents
In tests/create-preset.test.ts around lines 8 and 24, the test uses __dirname
which is undefined under ESM; replace uses of __dirname with a derived dirname
from import.meta.url (i.e., compute the file path via
fileURLToPath(import.meta.url) and then path.dirname(...) or equivalent) and
update the two occurrences so path.join uses that derived directory instead.
@@ -1,16 +1,15 @@ | |||
import { describe, it } from "mocha"; | |||
import { expect } from "chai"; | |||
import {afterAll, describe, it, expect} from "vitest"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Do not use async describe; move async setup into beforeAll hooks
Vitest doesn’t await async describe
bodies; current awaits may run unpredictably. Use beforeAll
for async setup.
-import {afterAll, describe, it, expect} from "vitest";
+import {afterAll, beforeAll, describe, it, expect} from "vitest";
@@
-describe("file-utils", async () => {
- const baseDir = path.resolve("tmp-files");
-
- await fs.ensureDir(baseDir);
+describe("file-utils", () => {
+ const baseDir = path.resolve("tmp-files");
+ beforeAll(async () => {
+ await fs.ensureDir(baseDir);
+ });
@@
- describe("replace tokens in files", async () => {
- fs.writeFileSync(path.join(baseDir, "files1.txt"), "test");
- fs.writeFileSync(path.join(baseDir, "files2.txt"), "test");
-
- await replaceTokensInFiles([{
- path: path.join(baseDir, "files1.txt"),
- tokens: [{
- from: "test",
- to: "test1",
- }],
- }, {
- path: path.join(baseDir, "files2.txt"),
- tokens: [{
- from: "test",
- to: "test2",
- }],
- }]);
+ describe("replace tokens in files", () => {
+ beforeAll(async () => {
+ fs.writeFileSync(path.join(baseDir, "files1.txt"), "test");
+ fs.writeFileSync(path.join(baseDir, "files2.txt"), "test");
+ await replaceTokensInFiles([
+ { path: path.join(baseDir, "files1.txt"), tokens: [{ from: "test", to: "test1" }] },
+ { path: path.join(baseDir, "files2.txt"), tokens: [{ from: "test", to: "test2" }] },
+ ]);
+ });
@@
- describe("replace tokens in file", async () => {
- fs.writeFileSync(path.join(baseDir, "file1.txt"), "test");
-
- await replaceTokensInFile({
- path: path.join(baseDir, "file1.txt"),
- tokens: [{
- from: "test",
- to: "test1",
- }],
- });
+ describe("replace tokens in file", () => {
+ beforeAll(async () => {
+ fs.writeFileSync(path.join(baseDir, "file1.txt"), "test");
+ await replaceTokensInFile({
+ path: path.join(baseDir, "file1.txt"),
+ tokens: [{ from: "test", to: "test1" }],
+ });
+ });
@@
- describe("get destination dir", async () => {
- const destDir = await getDestinationDir(path.join("tmp-files", "baz"));
+ describe("get destination dir", () => {
+ let destDir: string;
+ let destDir2: string;
+ beforeAll(async () => {
+ destDir = await getDestinationDir(path.join("tmp-files", "baz"));
+ destDir2 = await getDestinationDir(path.join("tmp-files", "baz"));
+ });
Also applies to: 11-16, 16-33, 43-53, 61-63
🤖 Prompt for AI Agents
In tests/file-utils.test.ts lines 1 and the ranges 11-16, 16-33, 43-53, 61-63:
the test file uses async describe bodies which Vitest does not await; change
each async describe to a plain synchronous describe and move any await/async
initialization inside beforeAll (and cleanup into afterAll) hooks at the top of
that describe block. Ensure any shared async variables are declared in outer
scope, assign them inside beforeAll using await, and remove awaits from the
describe callback so all describe callbacks are synchronous.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/build/build-distfiles.ts (1)
30-34
: Preserve both dependencies and peerDependencies; avoid JSON stringify/parse hack.Packages can have both; current else-if drops peers. Also replace only leading "workspace:" tokens.
- 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:", "")); - } + if (pkgInfo.dependencies) { + libObj.dependencies = Object.fromEntries( + Object.entries(pkgInfo.dependencies).map(([k, v]) => [k, v.replace(/^workspace:/, "")]), + ); + } + if (pkgInfo.peerDependencies) { + libObj.peerDependencies = Object.fromEntries( + Object.entries(pkgInfo.peerDependencies).map(([k, v]) => [k, v.replace(/^workspace:/, "")]), + ); + }
♻️ Duplicate comments (5)
tests/create-preset.test.ts (1)
6-31
: Serialize, add timeout, and always clean up temp dirs.Prevents CI flakiness and leaks; mirrors prior plugin test fix.
-describe("create-preset", () => { - it("should have created the preset project", async () => { +describe.sequential("create-preset", () => { + it("should have created the preset project", async () => { - const destDir = path.join(DIRNAME, "tmp-files", "foo-preset"); - - await createPresetTemplate("foo", "Foo", "", destDir); - - const pkgPath = path.join(destDir, "package.json"), - pkgInfo = await fs.readJSON(pkgPath); - - expect(pkgInfo.name).toBe("tsparticles-preset-foo"); - - await fs.remove(destDir); - }); + const destDir = path.join(DIRNAME, "tmp-files", "foo-preset"); + try { + await createPresetTemplate("foo", "Foo", "", destDir /* consider: { skipInstallBuild: true } */); + const pkgPath = path.join(destDir, "package.json"); + const pkgInfo = await fs.readJSON(pkgPath); + expect(pkgInfo.name).toBe("tsparticles-preset-foo"); + } finally { + await fs.remove(destDir); + } + }, 180000); @@ - it("should have created the preset project, w/ repo", async () => { - const destDir = path.join(DIRNAME, "tmp-files", "bar-preset"); - - await createPresetTemplate("bar", "Bar", "https://github.com/matteobruni/tsparticles", destDir); - - const pkgPath = path.join(destDir, "package.json"), - pkgInfo = await fs.readJSON(pkgPath); - - expect(pkgInfo.name).toBe("tsparticles-preset-bar"); - - await fs.remove(destDir); - }); + it("should have created the preset project, w/ repo", async () => { + const destDir = path.join(DIRNAME, "tmp-files", "bar-preset"); + try { + await createPresetTemplate("bar", "Bar", "https://github.com/matteobruni/tsparticles", destDir /* consider: { skipInstallBuild: true } */); + const pkgPath = path.join(destDir, "package.json"); + const pkgInfo = await fs.readJSON(pkgPath); + expect(pkgInfo.name).toBe("tsparticles-preset-bar"); + } finally { + await fs.remove(destDir); + } + }, 180000);Follow-up: if needed, I can add a
skipInstallBuild
option tocreatePresetTemplate
and update tests accordingly.files/empty-project/package.json (1)
81-83
: Prefer exports map and .cjs for CJS with "type":"module".Keeps Node resolution unambiguous; aligns ESM/CJS entry points.
- "main": "dist/cjs/index.js", - "module": "dist/esm/index.js", - "types": "dist/types/index.d.ts", + "exports": { + ".": { + "types": "./dist/types/index.d.ts", + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.cjs" + }, + "./package.json": "./package.json" + }, + "main": "./dist/esm/index.js", + "types": "dist/types/index.d.ts",Ensure the build emits
dist/cjs/index.cjs
(or rename).package.json (3)
79-79
: "main" should not point to eslint.config.js; set to CLI entry or drop.- "main": "eslint.config.js", + "main": "dist/cli.js", + "exports": { + ".": "./dist/cli.js", + "./package.json": "./package.json" + },
33-55
: Move build/lint tools to devDependencies to avoid bloating consumers."dependencies": { - "@tsparticles/eslint-config": "^3.0.5", - "@tsparticles/prettier-config": "^3.0.1", - "@tsparticles/tsconfig": "^3.0.5", - "@tsparticles/webpack-plugin": "^3.0.5", "commander": "^14.0.0", - "eslint": "^9.34.0", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-jsdoc": "^54.1.1", - "eslint-plugin-prettier": "^5.5.4", - "eslint-plugin-tsdoc": "^0.4.0", "fs-extra": "^11.3.1", "klaw": "^4.1.0", "lookpath": "^1.2.3", - "madge": "^8.0.0", - "path-scurry": "^2.0.0", - "prettier": "^3.6.2", - "prettier-plugin-multiline-arrays": "^4.0.3", "prompts": "^2.4.2", - "rimraf": "^6.0.1", - "typescript": "^5.9.2", - "typescript-eslint": "^8.41.0", - "webpack": "^5.101.3" + "path-scurry": "^2.0.0" }, "devDependencies": { + "@tsparticles/eslint-config": "^3.0.5", + "@tsparticles/prettier-config": "^3.0.1", + "@tsparticles/tsconfig": "^3.0.5", + "@tsparticles/webpack-plugin": "^3.0.5", + "eslint": "^9.34.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-jsdoc": "^54.1.1", + "eslint-plugin-prettier": "^5.5.4", + "eslint-plugin-tsdoc": "^0.4.0", + "madge": "^8.0.0", + "prettier": "^3.6.2", + "prettier-plugin-multiline-arrays": "^4.0.3", + "rimraf": "^6.0.1", + "typescript": "^5.9.2", + "typescript-eslint": "^8.41.0", + "webpack": "^5.101.3" }
57-59
: Remove self-dependency from devDependencies.- "@tsparticles/cli": "^2.3.3",
🧹 Nitpick comments (4)
files/create-shape/src/ShapeDrawer.ts (1)
4-5
: Embedding validTypes in the drawer matches the new API; tiny alloc nit.Hoist the array to a module-level const to avoid per-instance allocation (optionally freeze).
+const SHAPE_TYPES = Object.freeze(["#template#"] as const); export class ShapeDrawer implements IShapeDrawer { - readonly validTypes = ["#template#"] as const; + readonly validTypes = SHAPE_TYPES;src/build/build-eslint.ts (1)
28-30
: Avoid potential runtime error: prefer String() over toString() on possibly undefined fields.ESLint messages can occasionally omit line/column (e.g., file-level).
m.line.toString()
will throw;String(m.line)
safely coerces.- .map(m => `${t.filePath} (${m.line.toString()},${m.column.toString()}): ${m.message}`) + .map(m => `${t.filePath} (${String(m.line)},${String(m.column)}): ${m.message}`)src/build/build-distfiles.ts (2)
15-20
: Use fs-extra’s readJSON for simpler, safer parse (with encoding) and better errors.- const pkgInfo = JSON.parse((await fs.readFile(path.join(basePath, "package.json"))).toString()) as { + const pkgInfo = await fs.readJSON(path.join(basePath, "package.json")) as { dependencies?: Record<string, string>; peerDependencies?: Record<string, string>; publishConfig?: { directory?: string }; version: string; },
89-91
: Ensure submodule dirs exist before writing type package.json files.Avoid ENOENT on clean builds.
- await fs.writeFile(path.join(distPath, "cjs", "package.json"), `{ "type": "commonjs" }`); - await fs.writeFile(path.join(distPath, "esm", "package.json"), `{ "type": "module" }`); - await fs.writeFile(path.join(distPath, "browser", "package.json"), `{ "type": "module" }`); + await fs.ensureDir(path.join(distPath, "cjs")); + await fs.ensureDir(path.join(distPath, "esm")); + await fs.ensureDir(path.join(distPath, "browser")); + await fs.writeFile(path.join(distPath, "cjs", "package.json"), `{ "type": "commonjs" }`); + await fs.writeFile(path.join(distPath, "esm", "package.json"), `{ "type": "module" }`); + await fs.writeFile(path.join(distPath, "browser", "package.json"), `{ "type": "module" }`);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (13)
files/create-shape/src/ShapeDrawer.ts
(1 hunks)files/create-shape/src/index.ts
(1 hunks)files/empty-project/package.json
(2 hunks)package.json
(2 hunks)src/build/build-distfiles.ts
(1 hunks)src/build/build-diststats.ts
(1 hunks)src/build/build-eslint.ts
(1 hunks)src/cli.ts
(1 hunks)src/create/plugin/create-plugin.ts
(5 hunks)src/create/shape/create-shape.ts
(5 hunks)tests/create-plugin.test.ts
(1 hunks)tests/create-preset.test.ts
(1 hunks)tests/create-shape.test.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
- src/cli.ts
- src/build/build-diststats.ts
- src/create/plugin/create-plugin.ts
- tests/create-plugin.test.ts
- src/create/shape/create-shape.ts
- tests/create-shape.test.ts
🧰 Additional context used
🧬 Code graph analysis (2)
files/create-shape/src/index.ts (1)
files/create-shape/src/ShapeDrawer.ts (1)
ShapeDrawer
(3-16)
tests/create-preset.test.ts (2)
eslint.config.js (1)
__dirname
(6-6)src/create/preset/create-preset.ts (1)
createPresetTemplate
(180-204)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: pr
🔇 Additional comments (1)
files/create-shape/src/index.ts (1)
9-9
: No legacy two-arg addShape calls detected; migration verified.
import path from "path"; | ||
import fs from "fs-extra"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__dirname is undefined in ESM/Vitest — derive via import.meta.url.
import path from "path";
import fs from "fs-extra";
+import { fileURLToPath } from "node:url";
+const DIRNAME = path.dirname(fileURLToPath(import.meta.url));
@@
- const destDir = path.join(__dirname, "tmp-files", "foo-preset");
+ const destDir = path.join(DIRNAME, "tmp-files", "foo-preset");
@@
- const destDir = path.join(__dirname, "tmp-files", "bar-preset");
+ const destDir = path.join(DIRNAME, "tmp-files", "bar-preset");
Also applies to: 8-8, 21-21
🤖 Prompt for AI Agents
In tests/create-preset.test.ts around lines 3-4 (and also references at lines 8
and 21), the code uses __dirname which is undefined under ESM/Vitest; derive the
directory from import.meta.url instead: import fileURLToPath from 'url' (or
destructure fileURLToPath) and compute const __filename =
fileURLToPath(import.meta.url) and const __dirname = path.dirname(__filename),
then replace any direct __dirname usages on the mentioned lines with this
computed __dirname so the test resolves paths correctly in ESM.
Summary by CodeRabbit
New Features
Refactor
Tests
Chores