Skip to content

WIP: Refactor Out Deprecations#1

Open
martindale wants to merge 12 commits into
FabricLabs:masterfrom
martindale:feature/deprecations
Open

WIP: Refactor Out Deprecations#1
martindale wants to merge 12 commits into
FabricLabs:masterfrom
martindale:feature/deprecations

Conversation

@martindale
Copy link
Copy Markdown
Member

@martindale martindale commented May 17, 2026

Container for work on eliminating all deprecations. Initial work adds two new commands, report:install and a CI-safe equivalent report:install-ci as well as a reports/ directory to contain the reference artifact.

Summary by CodeRabbit

  • New Features

    • Docker images now hosted on GitHub Container Registry (ghcr.io/honkit/honkit); docs updated
  • Improvements

    • Live-reload revamped with a newer library for more reliable refreshes
    • Builds modernized to use esbuild for faster JS bundling
    • Replaced moment with dayjs for date/time handling
    • Require Node.js >= 20
    • Project packages bumped to 6.2.0
  • Bug Fixes

    • Safer object-path get/set to mitigate prototype-pollution
  • Documentation

    • Added reports README and install tracing workflow/tests

Review Change Stack

martindale and others added 6 commits May 16, 2026 19:35
* Add tests for Promise, objectPath

* refactor(honkit): drop q, object-path, moment, urijs

Replace q with a native Promise shim (promise.ts), object-path with safeObjectPath for config paths, moment with dayjs in defaultFilters, and urijs-free git+ URL parsing in git.ts.

Contract tests from the prior commit still pass (promise.contract, objectPath.contract). Remove promise.env-load.test.ts — it only covered Q.longStackSupport at load time (PR honkit#508 discussion r3231797881). Drop jest.contract coverageThreshold so test:contract passes with the shim under existing tests.

Add direct safeObjectPath unit tests. object-path remains a devDependency for the baseline contract only.

Suggested PR description update:
- Runtime removals: q, object-path, moment, urijs
- Additions: dayjs, js-yaml ^4, safeObjectPath helper, native promise surface
- Equivalence: contract tests + snapshot-honkit / integration suite
- fabric-http: run npm install in /Users/eric/fabric-http to capture transitive deprecation warnings motivating consumers
* fix: address review feedback from PR honkit#508

Follow-up to honkit#508 addressing Copilot review comments.

- safeObjectPath: reject whole path containing __proto__/constructor/prototype
  instead of filtering segments (prevented `__proto__.x` from collapsing to `x`)
- safeObjectPath: use Object.prototype.hasOwnProperty.call so lookups do not
  descend into inherited prototype properties
- safeObjectPath: setAtPath now throws on non-object scalar intermediates and
  returns undefined, matching object-path semantics
- safeObjectPath: accept `string | string[]` paths to match object-path API
- encodeConfig: align with new setAtPath void return type
- defaultFilters: load dayjs advancedFormat plugin so `Do` ordinal token works
  (e.g. `MMMM Do YYYY` now renders `May 16th 2026`)
- package.json: alphabetize dayjs position in dependencies
- git.ts: fix comment wording "infos" -> "information"
- tests: cover new own-property guard, unsafe path rejection, and array path

* fix(safeObjectPath): check enumerability not just ownership

`hasOwnProperty` returns true for non-enumerable own properties, so the
prior `hasOwn` guard let `getAtPath` and `setAtPath` still descend into
non-enumerable own data. Switch to `Object.prototype.propertyIsEnumerable`
so descent is genuinely limited to own enumerable properties, matching the
documented contract.

Adds tests covering non-enumerable own reads (treated as missing) and
non-enumerable own intermediates in writes (existing value left untouched).

* fix(safeObjectPath): match object-path own-check; throw on null intermediate

Adjust the safeObjectPath contract so the divergence from object-path is
limited to the path-level rejection of __proto__/constructor/prototype.

- Switch back to hasOwnProperty for own-property descent. object-path itself
  uses hasOwnProperty (does not check enumerability), so propertyIsEnumerable
  was stricter than the original library. Matching object-path is the
  intended contract.
- setAtPath now throws when an intermediate value is null. object-path threw
  here via the native "Cannot set properties of null" TypeError; the prior
  implementation silently overwrote null with a fresh container.
- Missing/undefined intermediates are still auto-created (object-path
  behavior); only existing scalar values (including null) throw.

Tests updated to cover the null-intermediate throw on both the new helper
and the object-path baseline.

* fix(safeObjectPath): treat array path entries as literal keys

When `path` is a `string[]`, each entry is a caller-supplied property name,
including the empty string. Previously the segment normalizer trimmed and
filtered empty entries uniformly, which rewrote paths like `["", "key"]`
into just `["key"]` and addressed an unrelated property.

Now whitespace trimming and empty-segment filtering only apply to string
paths (where they remove dot-split artifacts). Array entries are passed
through verbatim, matching object-path's lookup behavior.
Co-authored-by: azu <19714+azu@users.noreply.github.com>
Switch the published Docker image from `honkit/honkit` on Docker Hub to
`ghcr.io/honkit/honkit` on GitHub Container Registry. This removes the
need for Docker Hub credentials (DOCKER_HUB_USER/DOCKER_HUB_TOKEN) and
uses the built-in GITHUB_TOKEN for authentication.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.12 to 5.0.6.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](juliangruber/brace-expansion@v1.1.12...v5.0.6)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 5.0.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: azu <19714+azu@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7e160119-d628-47c6-bdb8-be6febed9f8f

📥 Commits

Reviewing files that changed from the base of the PR and between 920c281 and e173cf9.

📒 Files selected for processing (1)
  • packages/honkit/src/cli/serve.ts

📝 Walkthrough

Walkthrough

This PR modernizes HonKit's build infrastructure and core dependencies by migrating container publishing to GitHub Container Registry, replacing external libraries (q, moment, urijs, object-path, cp/cpr, tiny-lr) with native or minimal alternatives, and updating build tooling from browserify to esbuild.

Changes

Release Infrastructure and Dependency Modernization

Layer / File(s) Summary
Release infrastructure and documentation migration to GHCR
.github/workflows/release.yml, README.md, docker/README.md, reports/README.md, .github/workflows/test.yml, package.json
GitHub Actions release workflow and container documentation migrated from Docker Hub to GitHub Container Registry (GHCR). Installation reporting infrastructure added with report:install script and CI trace-deprecation job.
Version bumps across monorepo packages
package.json, examples/*/package.json, packages/@honkit/*/package.json, packages/honkit/package.json
All package versions updated from 6.1.7 to 6.2.0 across root, examples, and utility packages to maintain consistency.
Core dependency updates and feature additions
packages/honkit/package.json, packages/honkit/jest.contract.config.js
Root package updated: dayjs added, js-yaml→^4.1.0, moment/q/urijs/tiny-lr removed as runtime deps, object-path moved to devDependencies. Node engine updated to >=20. Contract test infrastructure added with jest.contract.config.js.
Safe object path utility with prototype-pollution guards
packages/honkit/src/utils/safeObjectPath.ts, packages/honkit/src/utils/__tests__/safeObjectPath.ts, packages/honkit/src/utils/__tests__/objectPath.contract.test.ts
New safeObjectPath.ts provides getAtPath/setAtPath helpers with prototype-pollution safety. Comprehensive unit tests and contract tests documenting baseline object-path behavior and safe alternatives.
Migrate from object-path to safe helpers
packages/honkit/src/api/deprecate.ts, packages/honkit/src/api/encodeConfig.ts
Update deprecate.ts and encodeConfig.ts to use new safeObjectPath getAtPath/setAtPath instead of external object-path dependency.
Promise utilities rewrite: Q library replaced with native Promise
packages/honkit/src/utils/promise.ts, packages/honkit/src/utils/__tests__/promise.contract.test.ts
Rewrite promise.ts to remove q dependency and provide ExtendedPromise type wrapping native Promise with Q-compatible chaining methods (thenResolve, spread, fail, tap, progress, nodeify, fin). Implement defer, nfcall, nfbind, and async collection helpers. Extensive contract tests verify deferred resolution, node-style callbacks, progress notifications, and Immutable collection handling.
Date/time library: moment replaced with dayjs
packages/honkit/src/constants/defaultFilters.ts
Switch defaultFilters.ts from moment to dayjs for date formatting and relative-time expressions with relativeTime and advancedFormat plugins.
Utility modernization: Git URLs, YAML serialization, live-reload
packages/honkit/src/utils/git.ts, packages/honkit/src/models/page.ts, packages/honkit/src/models/fs.ts, packages/honkit/src/cli/serve.ts
Replace urijs-based git URL parsing with manual string parsing. Update YAML serialization from safeDump to dump. Switch live-reload from tiny-lr to livereload with updated trigger mechanism. Update fs.ts readAsString encoding type to BufferEncoding.
HTML module refactoring: Extract loadXml and centralize DOM APIs
packages/@honkit/html/src/dom.ts, packages/@honkit/html/src/index.ts, packages/honkit/src/output/modifiers/annotateText.ts, packages/honkit/src/output/modifiers/fetchRemoteImages.ts, packages/honkit/src/output/modifiers/inlineSvg.ts, packages/honkit/src/output/modifiers/modifyHTML.ts
New loadXml function in @honkit/html for XML/SVG loading. Export HtmlDom type alias. Update all output modifiers to use HtmlDom type from @honkit/html instead of importing cheerio directly, centralizing DOM API contract.
File system utilities: Replace cp/cpr with native fs.promises
packages/honkit/src/utils/fs.ts
Replace cp/cpr external imports with internal helpers (copyFile, copyDirRecursive, copyDir) built on fs.promises. Helpers create destination directories and recursively copy contents with optional delete-first behavior.
Build tooling updates: browserify→esbuild, dependency upgrades
packages/@honkit/theme-default/package.json, packages/@honkit/theme/package.json, packages/@honkit/html/package.json, packages/@honkit/markup-it/package.json
Theme-default build scripts switch from browserify+uglifyjs to esbuild. Upgrade less to ^4.2.0, mocha to ^11.7.4, jsdom to ^26.1.0. Remove browserify/uglify-js.
Example configuration: Remove deprecated github-issue-feedback plugin
examples/benchmark/book.json, examples/benchmark/package.json
Remove github-issue-feedback GitBook plugin from benchmark example and its devDependencies, cleaning up deprecated plugin references.

🎯 4 (Complex) | ⏱️ ~60 minutes

🐰 A rabbit's ode to modernization:
From moment to dayjs, our time's more light,
Native promises now glow so bright,
GHCR beckons, Docker Hub's past,
Dependencies pruned, this build's built to last! 🚀

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title "WIP: Refactor Out Deprecations" is a significant oversimplification that does not accurately reflect the scope and nature of the changes. The changeset is a comprehensive refactoring that removes multiple deprecated dependencies (object-path, moment, q, urijs, tiny-lr, browserify, uglify-js) and replaces them with modern alternatives (dayjs, native Promise extensions, custom parsers, esbuild, livereload). It also includes container registry migration, dependency updates, test additions, and utility rewrites—none of which are clearly conveyed by the vague title. The title is too generic and uses a non-descriptive term ("Refactor Out Deprecations") that obscures the actual technical work performed. Revise the title to be more specific and descriptive of the major changes, such as: "Replace deprecated dependencies with modern alternatives (dayjs, native Promises, esbuild) and migrate container registry to GHCR" or similar, to better guide code reviewers.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with 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.

❤️ Share

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

@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented May 17, 2026

Not up to standards ⛔

🔴 Issues 4 critical · 6 high · 11 medium · 1 minor

Alerts:
⚠ 22 issues (≤ 0 issues of at least minor severity)

Results:
22 new issues

Category Results
BestPractice 8 medium
ErrorProne 2 high
Security 4 critical
4 high
CodeStyle 1 minor
Complexity 3 medium

View in Codacy

🟢 Metrics 204 complexity · 2 duplication

Metric Results
Complexity 204
Duplication 2

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@martindale martindale force-pushed the feature/deprecations branch from 9a78d03 to 874a52b Compare May 17, 2026 06:28
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (1)
packages/honkit/src/utils/__tests__/promise.contract.test.ts (1)

111-120: ⚡ Quick win

This test pre-binds away the behavior it is trying to verify.

Because ctx.run.bind(ctx) is already bound before it reaches nfbind, this still passes even if nfbind stops preserving this. Use the unbound method and invoke the wrapper with .call(ctx) so the contract is actually exercised.

Proposed fix
-        const bound = PromiseUtil.nfbind(ctx.run.bind(ctx));
-        await expect(bound()).resolves.toBe(3);
+        const bound = PromiseUtil.nfbind(ctx.run);
+        await expect(bound.call(ctx)).resolves.toBe(3);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/honkit/src/utils/__tests__/promise.contract.test.ts` around lines
111 - 120, The test "nfbind binds this" incorrectly pre-binds the method before
passing it to PromiseUtil.nfbind; update the test to pass the unbound method
(use PromiseUtil.nfbind(ctx.run)) and then invoke the returned wrapper with
.call(ctx) (e.g., const bound = PromiseUtil.nfbind(ctx.run); await
expect(bound.call(ctx)).resolves.toBe(3)); this ensures nfbind must preserve the
original this when invoked.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docker/README.md`:
- Line 13: The markdown examples currently include a leading shell prompt "$"
which triggers MD014; remove the leading "$" from each command example (e.g.,
change "$ docker run -v `pwd`:`pwd` -w `pwd` --rm -it ghcr.io/honkit/honkit
honkit --help" and the other similar lines) so the README contains plain command
lines without prompt symbols, keeping output semantics the same while satisfying
markdownlint.

In `@packages/honkit/src/api/deprecate.ts`:
- Around line 109-113: In deprecateRenamedMethod, ensure the resolved target
from getAtPath is callable before wrapping it: after computing fn (from
getAtPath(instance, newName)), check typeof fn === "function" (or fn instanceof
Function) and if not throw a clear TypeError/RangeError that includes the book,
key, oldName and newName so the failure is fast and informative; then pass the
validated fn into deprecateMethod and assign to instance[oldName] as before.

In `@packages/honkit/src/utils/git.ts`:
- Around line 121-129: The current code uses the first regex match
(gitSuffix.exec(giturl)) which picks the earliest ".git" and misparses URLs with
intermediate segments like "team.git/repo.git"; change the logic to locate the
last ".git" repository boundary instead: compute the last match (e.g., using
Array.from(giturl.matchAll(gitSuffix)) and taking the final match or using
giturl.lastIndexOf('.git') and validating the following char is '/' or end) and
then set host and filepath from that last match index (update uses of m.index to
the last match index) so host = giturl.slice(0, lastIndex + 4) and filepath =
giturl.slice(lastIndex + 4).

In `@packages/honkit/src/utils/promise.ts`:
- Around line 202-218: The current some() coerces every non-List input into
Immutable.List, which strips keys from keyed Immutable.Iterable (e.g.,
Map/OrderedMap) and breaks key-dependent predicates; change the conversion logic
so that if arr is an Immutable.Iterable you keep it as-is and only convert plain
JS arrays to Immutable.List, then call reduce on that Iterable (so keyed
iterables preserve their keys when iter is invoked). Update the code paths
around the list variable and the reduce call in function some to use the
original Immutable.Iterable when Immutable.Iterable.isIterable(arr) is true,
otherwise wrap plain arrays with Immutable.List, and keep using
extendPromise(Promise.resolve(undefined)) as the initial accumulator.

In `@README.md`:
- Line 61: Edit the README sentence that currently reads "Honkit provide docker
image at [ghcr.io/honkit/honkit]" and correct the subject-verb agreement and
casing by changing it to "HonKit provides docker image at
[ghcr.io/honkit/honkit]"; locate the exact string "Honkit provide docker image"
and replace it with "HonKit provides docker image" to fix the grammar and proper
project name capitalization.
- Around line 65-69: Update the fenced code block containing the three docker
commands so it includes a language identifier (e.g., add ```bash at the opening
fence) instead of a plain ```; specifically modify the block that currently
wraps the docker pull/run lines to start with ```bash and keep the closing ```
unchanged so markdownlint MD040 is satisfied.

---

Nitpick comments:
In `@packages/honkit/src/utils/__tests__/promise.contract.test.ts`:
- Around line 111-120: The test "nfbind binds this" incorrectly pre-binds the
method before passing it to PromiseUtil.nfbind; update the test to pass the
unbound method (use PromiseUtil.nfbind(ctx.run)) and then invoke the returned
wrapper with .call(ctx) (e.g., const bound = PromiseUtil.nfbind(ctx.run); await
expect(bound.call(ctx)).resolves.toBe(3)); this ensures nfbind must preserve the
original this when invoked.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1a2de399-6f21-4637-8e34-0a0f6977bed1

📥 Commits

Reviewing files that changed from the base of the PR and between 8ff3f5e and 9a78d03.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • reports/install.log is excluded by !**/*.log
📒 Files selected for processing (33)
  • .github/workflows/release.yml
  • README.md
  • docker/README.md
  • examples/benchmark/package.json
  • examples/book/package.json
  • examples/multiple-languages/package.json
  • package.json
  • packages/@honkit/asciidoc/package.json
  • packages/@honkit/cleaning-tools/package.json
  • packages/@honkit/honkit-plugin-fontsettings/package.json
  • packages/@honkit/honkit-plugin-highlight/package.json
  • packages/@honkit/html/package.json
  • packages/@honkit/internal-test-utils/package.json
  • packages/@honkit/markdown-legacy/package.json
  • packages/@honkit/markdown/package.json
  • packages/@honkit/markup-it/package.json
  • packages/@honkit/theme-default/package.json
  • packages/@honkit/theme/package.json
  • packages/honkit/jest.contract.config.js
  • packages/honkit/package.json
  • packages/honkit/src/api/deprecate.ts
  • packages/honkit/src/api/encodeConfig.ts
  • packages/honkit/src/constants/defaultFilters.ts
  • packages/honkit/src/models/fs.ts
  • packages/honkit/src/models/page.ts
  • packages/honkit/src/utils/__tests__/objectPath.contract.test.ts
  • packages/honkit/src/utils/__tests__/promise.contract.test.ts
  • packages/honkit/src/utils/__tests__/safeObjectPath.ts
  • packages/honkit/src/utils/git.ts
  • packages/honkit/src/utils/promise.ts
  • packages/honkit/src/utils/safeObjectPath.ts
  • reports/README.md
  • scripts/report-install-ci.sh

Comment thread docker/README.md
Show help

$ docker run -v `pwd`:`pwd` -w `pwd` --rm -it honkit/honkit honkit --help
$ docker run -v `pwd`:`pwd` -w `pwd` --rm -it ghcr.io/honkit/honkit honkit --help
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove leading $ from command examples to satisfy markdownlint.

These lines trigger MD014; dropping prompt symbols keeps docs lint-clean without changing meaning.

🧹 Proposed fix
-    $ docker run -v `pwd`:`pwd` -w `pwd` --rm -it ghcr.io/honkit/honkit honkit --help
+    docker run -v `pwd`:`pwd` -w `pwd` --rm -it ghcr.io/honkit/honkit honkit --help
-    $ docker run -v `pwd`:`pwd` -w `pwd` --rm -it ghcr.io/honkit/honkit honkit build
+    docker run -v `pwd`:`pwd` -w `pwd` --rm -it ghcr.io/honkit/honkit honkit build
-    $ docker run -v `pwd`:`pwd` -w `pwd` --rm -it ghcr.io/honkit/honkit honkit pdf
+    docker run -v `pwd`:`pwd` -w `pwd` --rm -it ghcr.io/honkit/honkit honkit pdf
-    $ docker run -it --init -p 4000:4000  -v `pwd`:`pwd` -w `pwd` --rm  ghcr.io/honkit/honkit honkit serve
+    docker run -it --init -p 4000:4000  -v `pwd`:`pwd` -w `pwd` --rm  ghcr.io/honkit/honkit honkit serve

Also applies to: 17-17, 21-21, 25-25

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 13-13: Dollar signs used before commands without showing output

(MD014, commands-show-output)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker/README.md` at line 13, The markdown examples currently include a
leading shell prompt "$" which triggers MD014; remove the leading "$" from each
command example (e.g., change "$ docker run -v `pwd`:`pwd` -w `pwd` --rm -it
ghcr.io/honkit/honkit honkit --help" and the other similar lines) so the README
contains plain command lines without prompt symbols, keeping output semantics
the same while satisfying markdownlint.

Comment on lines 109 to 113
function deprecateRenamedMethod(book, key, instance, oldName, newName, msg) {
msg = msg || `"${oldName}" is deprecated, use "${newName}()" instead`;
const fn = objectPath.get(instance, newName);
const fn = getAtPath(instance as Record<string, unknown>, newName) as (...args: unknown[]) => unknown;

instance[oldName] = deprecateMethod(book, key, fn, msg);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate the renamed target resolves to a callable before wrapping.

Line 111 can resolve a non-function/undefined, which then crashes later in deprecateMethod at call time (fn.apply). Fail fast here with a clear error.

Proposed fix
 function deprecateRenamedMethod(book, key, instance, oldName, newName, msg) {
     msg = msg || `"${oldName}" is deprecated, use "${newName}()" instead`;
-    const fn = getAtPath(instance as Record<string, unknown>, newName) as (...args: unknown[]) => unknown;
+    const fn = getAtPath(instance as Record<string, unknown>, newName);
+    if (!is.fn(fn)) {
+        throw new TypeError(
+            `Cannot deprecate "${oldName}": "${newName}" does not resolve to a function`
+        );
+    }
 
-    instance[oldName] = deprecateMethod(book, key, fn, msg);
+    instance[oldName] = deprecateMethod(book, key, fn, msg);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function deprecateRenamedMethod(book, key, instance, oldName, newName, msg) {
msg = msg || `"${oldName}" is deprecated, use "${newName}()" instead`;
const fn = objectPath.get(instance, newName);
const fn = getAtPath(instance as Record<string, unknown>, newName) as (...args: unknown[]) => unknown;
instance[oldName] = deprecateMethod(book, key, fn, msg);
function deprecateRenamedMethod(book, key, instance, oldName, newName, msg) {
msg = msg || `"${oldName}" is deprecated, use "${newName}()" instead`;
const fn = getAtPath(instance as Record<string, unknown>, newName);
if (!is.fn(fn)) {
throw new TypeError(
`Cannot deprecate "${oldName}": "${newName}" does not resolve to a function`
);
}
instance[oldName] = deprecateMethod(book, key, fn, msg);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/honkit/src/api/deprecate.ts` around lines 109 - 113, In
deprecateRenamedMethod, ensure the resolved target from getAtPath is callable
before wrapping it: after computing fn (from getAtPath(instance, newName)),
check typeof fn === "function" (or fn instanceof Function) and if not throw a
clear TypeError/RangeError that includes the book, key, oldName and newName so
the failure is fast and informative; then pass the validated fn into
deprecateMethod and assign to instance[oldName] as before.

Comment on lines +121 to +129
// Match a real repository ".git" suffix (not e.g. "gist.github.com" where ".git" appears inside the hostname)
const gitSuffix = /\.git(\/|$)/;
const m = gitSuffix.exec(giturl);
if (!m) {
return null;
}

// Recreate pathname without the real filename
uri.path(`${fileParts[0]}.git`);
const host = giturl.slice(0, m.index + 4);
let filepath = giturl.slice(m.index + 4);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Select the last .git repository boundary, not the first.

Using the first .git(/|$) match misparses valid URLs with namespace segments ending in .git (e.g., .../team.git/repo.git/...), which breaks clone/resolve.

Proposed fix
-    const gitSuffix = /\.git(\/|$)/;
-    const m = gitSuffix.exec(giturl);
-    if (!m) {
+    const gitSuffix = /\.git(\/|$)/g;
+    let m: RegExpExecArray | null = null;
+    let repoMatch: RegExpExecArray | null = null;
+    while ((m = gitSuffix.exec(giturl)) !== null) {
+        repoMatch = m;
+    }
+    if (!repoMatch) {
         return null;
     }
 
-    const host = giturl.slice(0, m.index + 4);
-    let filepath = giturl.slice(m.index + 4);
+    const host = giturl.slice(0, repoMatch.index + 4);
+    let filepath = giturl.slice(repoMatch.index + 4);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Match a real repository ".git" suffix (not e.g. "gist.github.com" where ".git" appears inside the hostname)
const gitSuffix = /\.git(\/|$)/;
const m = gitSuffix.exec(giturl);
if (!m) {
return null;
}
// Recreate pathname without the real filename
uri.path(`${fileParts[0]}.git`);
const host = giturl.slice(0, m.index + 4);
let filepath = giturl.slice(m.index + 4);
// Match a real repository ".git" suffix (not e.g. "gist.github.com" where ".git" appears inside the hostname)
const gitSuffix = /\.git(\/|$)/g;
let m: RegExpExecArray | null = null;
let repoMatch: RegExpExecArray | null = null;
while ((m = gitSuffix.exec(giturl)) !== null) {
repoMatch = m;
}
if (!repoMatch) {
return null;
}
const host = giturl.slice(0, repoMatch.index + 4);
let filepath = giturl.slice(repoMatch.index + 4);
🧰 Tools
🪛 OpenGrep (1.20.0)

[ERROR] 123-123: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/honkit/src/utils/git.ts` around lines 121 - 129, The current code
uses the first regex match (gitSuffix.exec(giturl)) which picks the earliest
".git" and misparses URLs with intermediate segments like "team.git/repo.git";
change the logic to locate the last ".git" repository boundary instead: compute
the last match (e.g., using Array.from(giturl.matchAll(gitSuffix)) and taking
the final match or using giturl.lastIndexOf('.git') and validating the following
char is '/' or end) and then set host and filepath from that last match index
(update uses of m.index to the last match index) so host = giturl.slice(0,
lastIndex + 4) and filepath = giturl.slice(lastIndex + 4).

Comment on lines +202 to +218
function some<T>(
arr: Immutable.List<T> | unknown[] | Immutable.Iterable<T, unknown>,
iter: (el: T, i: unknown) => unknown
) {
const list = Immutable.List.isList(arr) ? (arr as Immutable.List<T>) : Immutable.List(arr as T[]);

return list.reduce(
(prev: ExtendedPromise<unknown>, elem: T, i: number) => {
return prev.then((val) => {
if (val) {
return val;
}
return iter(elem, i);
});
},
extendPromise(Promise.resolve(undefined))
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve keyed Immutable iterables in some().

Line 206 coerces every non-List input into Immutable.List, so Immutable.Map/OrderedMap lose their original keys before iter runs. That changes the exported Immutable.Iterable contract and can return the wrong match for key-dependent predicates.

Proposed fix
 function some<T>(
     arr: Immutable.List<T> | unknown[] | Immutable.Iterable<T, unknown>,
     iter: (el: T, i: unknown) => unknown
 ) {
-    const list = Immutable.List.isList(arr) ? (arr as Immutable.List<T>) : Immutable.List(arr as T[]);
+    const list = Immutable.Iterable.isIterable(arr) ? arr : Immutable.List(arr as T[]);

     return list.reduce(
         (prev: ExtendedPromise<unknown>, elem: T, i: number) => {
             return prev.then((val) => {
                 if (val) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function some<T>(
arr: Immutable.List<T> | unknown[] | Immutable.Iterable<T, unknown>,
iter: (el: T, i: unknown) => unknown
) {
const list = Immutable.List.isList(arr) ? (arr as Immutable.List<T>) : Immutable.List(arr as T[]);
return list.reduce(
(prev: ExtendedPromise<unknown>, elem: T, i: number) => {
return prev.then((val) => {
if (val) {
return val;
}
return iter(elem, i);
});
},
extendPromise(Promise.resolve(undefined))
);
function some<T>(
arr: Immutable.List<T> | unknown[] | Immutable.Iterable<T, unknown>,
iter: (el: T, i: unknown) => unknown
) {
const list = Immutable.Iterable.isIterable(arr) ? arr : Immutable.List(arr as T[]);
return list.reduce(
(prev: ExtendedPromise<unknown>, elem: T, i: number) => {
return prev.then((val) => {
if (val) {
return val;
}
return iter(elem, i);
});
},
extendPromise(Promise.resolve(undefined))
);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/honkit/src/utils/promise.ts` around lines 202 - 218, The current
some() coerces every non-List input into Immutable.List, which strips keys from
keyed Immutable.Iterable (e.g., Map/OrderedMap) and breaks key-dependent
predicates; change the conversion logic so that if arr is an Immutable.Iterable
you keep it as-is and only convert plain JS arrays to Immutable.List, then call
reduce on that Iterable (so keyed iterables preserve their keys when iter is
invoked). Update the code paths around the list variable and the reduce call in
function some to use the original Immutable.Iterable when
Immutable.Iterable.isIterable(arr) is true, otherwise wrap plain arrays with
Immutable.List, and keep using extendPromise(Promise.resolve(undefined)) as the
initial accumulator.

Comment thread README.md
## Docker support

Honkit provide docker image at [honkit/honkit](https://hub.docker.com/r/honkit/honkit).
Honkit provide docker image at [ghcr.io/honkit/honkit](https://github.com/honkit/honkit/pkgs/container/honkit).
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix subject-verb agreement in the Docker support sentence.

Line 61 should read “HonKit provides …” for correct user-facing grammar.

✏️ Proposed fix
-Honkit provide docker image at [ghcr.io/honkit/honkit](https://github.com/honkit/honkit/pkgs/container/honkit).
+HonKit provides a Docker image at [ghcr.io/honkit/honkit](https://github.com/honkit/honkit/pkgs/container/honkit).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Honkit provide docker image at [ghcr.io/honkit/honkit](https://github.com/honkit/honkit/pkgs/container/honkit).
HonKit provides a Docker image at [ghcr.io/honkit/honkit](https://github.com/honkit/honkit/pkgs/container/honkit).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` at line 61, Edit the README sentence that currently reads "Honkit
provide docker image at [ghcr.io/honkit/honkit]" and correct the subject-verb
agreement and casing by changing it to "HonKit provides docker image at
[ghcr.io/honkit/honkit]"; locate the exact string "Honkit provide docker image"
and replace it with "HonKit provides docker image" to fix the grammar and proper
project name capitalization.

Comment thread README.md
Comment on lines 65 to 69
```
docker pull honkit/honkit
docker run -v `pwd`:`pwd` -w `pwd` --rm -it honkit/honkit honkit build
docker run -v `pwd`:`pwd` -w `pwd` --rm -it honkit/honkit honkit pdf
docker pull ghcr.io/honkit/honkit
docker run -v `pwd`:`pwd` -w `pwd` --rm -it ghcr.io/honkit/honkit honkit build
docker run -v `pwd`:`pwd` -w `pwd` --rm -it ghcr.io/honkit/honkit honkit pdf
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a language to the fenced command block.

The fenced block is missing a language marker, which triggers markdownlint (MD040).

🧩 Proposed fix
-```
+```bash
 docker pull ghcr.io/honkit/honkit
 docker run -v `pwd`:`pwd` -w `pwd` --rm -it ghcr.io/honkit/honkit honkit build
 docker run -v `pwd`:`pwd` -w `pwd` --rm -it ghcr.io/honkit/honkit honkit pdf
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.22.1)</summary>

[warning] 65-65: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

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

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @README.md around lines 65 - 69, Update the fenced code block containing the
three docker commands so it includes a language identifier (e.g., add bash at the opening fence) instead of a plain ; specifically modify the block that
currently wraps the docker pull/run lines to start with bash and keep the closing unchanged so markdownlint MD040 is satisfied.


</details>

<!-- fingerprinting:phantom:poseidon:hawk -->

<!-- This is an auto-generated comment by CodeRabbit -->

Replace cpr/cp directory copies with Node fs helpers, swap tiny-lr for
livereload (no raw-body chain), and require Node >=20. Refresh lockfile
and reports/install.log after report:install.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/honkit/src/cli/serve.ts`:
- Around line 200-205: The current check only prevents creating the livereload
server but doesn't disable live injection elsewhere; update the logic so lrport
=== 0 is treated as fully disabling live reload by propagating that state to the
rest of the flow (e.g., set kwargs.live = false or clear hasLiveReloading)
before returning; locate the block around hasWatch / hasLiveReloading /
kwargs.lrport and ensure startServer() and generateBook() see live as disabled
(or make generateBook()/startServer() also check kwargs.lrport === 0 before
injecting the livereload plugin) so no livereload snippet/plugin is injected
when lrport is 0.

In `@packages/honkit/src/utils/fs.ts`:
- Around line 197-199: The current delete-first branch swallows all errors by
appending .catch(() => undefined) to fs.promises.rm(dest, { recursive: true,
force: true }), which hides permission/I/O failures; remove the catch and let
the promise reject so callers see real errors (i.e., replace await
fs.promises.rm(dest, { recursive: true, force: true }).catch(() => undefined)
with a plain await fs.promises.rm(dest, { recursive: true, force: true }) so
opts.deleteFirst, fs.promises.rm and dest are used but failures are not silently
ignored).
- Around line 196-206: Detect and block self- or nested-destination copies at
the start of copyDirRecursive: resolve src and dest with path.resolve and if
they are equal (resolvedSrc === resolvedDest) throw or return early to avoid
deleting the source when opts.deleteFirst is true; also detect if resolvedDest
is inside resolvedSrc (resolvedDest.startsWith(resolvedSrc + path.sep)) and fail
fast (throw an error) to prevent recursing into a destination nested under the
source. Additionally, when iterating entries in copyDirRecursive (function
copyDirRecursive), skip any entry whose resolved source path equals or is inside
resolvedDest to avoid copying into the newly-created destination directory.
Ensure references to src, dest, opts.deleteFirst, and function name
copyDirRecursive are used so the checks are added in the right place.

In `@reports/README.md`:
- Line 6: Update the table row that starts with "Fresh install log (wiped
`node_modules`, regenerated lockfile)" so the output path cell shows
`reports/install.log` instead of `install.log` to match the actual script
output; locate and edit the Markdown table row containing the text `Fresh
install log` and replace the backticked `install.log` with
`reports/install.log`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6b712da8-b53b-485b-9f30-3f5163ffaaaa

📥 Commits

Reviewing files that changed from the base of the PR and between 9a78d03 and 920c281.

⛔ Files ignored due to path filters (3)
  • packages/honkit/src/__tests__/__snapshots__/snapshot-honkit.ts.snap is excluded by !**/*.snap
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • reports/install.log is excluded by !**/*.log
📒 Files selected for processing (18)
  • .github/workflows/test.yml
  • examples/benchmark/book.json
  • examples/benchmark/package.json
  • package.json
  • packages/@honkit/html/package.json
  • packages/@honkit/html/src/dom.ts
  • packages/@honkit/html/src/index.ts
  • packages/@honkit/markup-it/package.json
  • packages/@honkit/theme-default/package.json
  • packages/@honkit/theme/package.json
  • packages/honkit/package.json
  • packages/honkit/src/cli/serve.ts
  • packages/honkit/src/output/modifiers/annotateText.ts
  • packages/honkit/src/output/modifiers/fetchRemoteImages.ts
  • packages/honkit/src/output/modifiers/inlineSvg.ts
  • packages/honkit/src/output/modifiers/modifyHTML.ts
  • packages/honkit/src/utils/fs.ts
  • reports/README.md
💤 Files with no reviewable changes (1)
  • examples/benchmark/package.json
✅ Files skipped from review due to trivial changes (3)
  • packages/honkit/src/output/modifiers/fetchRemoteImages.ts
  • packages/@honkit/html/package.json
  • packages/@honkit/html/src/dom.ts

Comment thread packages/honkit/src/cli/serve.ts Outdated
Comment on lines +200 to +205
if (!hasWatch || !hasLiveReloading || kwargs.lrport === 0) {
return;
}

lrServer = tinylr({});

return Promise.nfcall(lrServer.listen.bind(lrServer), kwargs.lrport).then(() => {
console.log("Live reload server started on port:", kwargs.lrport);
console.log("Press CTRL+C to quit ...");
console.log("");
lrServer = livereload.createServer({
port: kwargs.lrport
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Propagate lrport === 0 as a real live-reload disable.

Line 200 only skips the server bootstrap. startServer() / generateBook() still treat kwargs["live"] as enabled, so --lrport=0 still injects the livereload plugin into generated pages while no LiveReload server exists.

Suggested fix
 function startServer(args, kwargs) {
@@
-    const hasLiveReloading = kwargs["live"];
+    const hasLiveReloading = kwargs["live"] && kwargs.lrport !== 0;
@@
     exec: function (args, kwargs) {
         server = new Server();
         const hasWatch = kwargs["watch"];
-        const hasLiveReloading = kwargs["live"];
+        const hasLiveReloading = kwargs["live"] && kwargs.lrport !== 0;
@@
-                if (!hasWatch || !hasLiveReloading || kwargs.lrport === 0) {
+                if (!hasWatch || !hasLiveReloading) {
                     return;
                 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/honkit/src/cli/serve.ts` around lines 200 - 205, The current check
only prevents creating the livereload server but doesn't disable live injection
elsewhere; update the logic so lrport === 0 is treated as fully disabling live
reload by propagating that state to the rest of the flow (e.g., set kwargs.live
= false or clear hasLiveReloading) before returning; locate the block around
hasWatch / hasLiveReloading / kwargs.lrport and ensure startServer() and
generateBook() see live as disabled (or make generateBook()/startServer() also
check kwargs.lrport === 0 before injecting the livereload plugin) so no
livereload snippet/plugin is injected when lrport is 0.

Comment on lines +196 to +206
async function copyDirRecursive(src, dest, opts) {
if (opts.deleteFirst) {
await fs.promises.rm(dest, { recursive: true, force: true }).catch(() => undefined);
}
await fs.promises.mkdir(dest, { recursive: true });
const entries = await fs.promises.readdir(src, { withFileTypes: true });
for (const entry of entries) {
const from = path.join(src, entry.name);
const to = path.join(dest, entry.name);
if (entry.isDirectory()) {
await copyDirRecursive(from, to, { ...opts, deleteFirst: false });
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Block self-copies before mutating the destination.

If src and dest resolve to the same path, Line 198 deletes the source tree when deleteFirst is set. If dest is nested under src, Lines 200-206 recurse into the directory being created and can keep copying until path-length or disk limits stop it.

🛑 Proposed guard
 async function copyDirRecursive(src, dest, opts) {
+    const resolvedSrc = path.resolve(src);
+    const resolvedDest = path.resolve(dest);
+    const relativeDest = path.relative(resolvedSrc, resolvedDest);
+    const destIsSameOrNested =
+        relativeDest === "" ||
+        (!relativeDest.startsWith("..") && !path.isAbsolute(relativeDest));
+
+    if (destIsSameOrNested) {
+        throw new Error("Destination must not be the source directory or a child of it");
+    }
+
     if (opts.deleteFirst) {
         await fs.promises.rm(dest, { recursive: true, force: true }).catch(() => undefined);
     }
🧰 Tools
🪛 GitHub Check: Codacy Static Code Analysis

[failure] 203-203: packages/honkit/src/utils/fs.ts#L203
Detected possible user input going into a path.join or path.resolve function.


[failure] 204-204: packages/honkit/src/utils/fs.ts#L204
Detected possible user input going into a path.join or path.resolve function.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/honkit/src/utils/fs.ts` around lines 196 - 206, Detect and block
self- or nested-destination copies at the start of copyDirRecursive: resolve src
and dest with path.resolve and if they are equal (resolvedSrc === resolvedDest)
throw or return early to avoid deleting the source when opts.deleteFirst is
true; also detect if resolvedDest is inside resolvedSrc
(resolvedDest.startsWith(resolvedSrc + path.sep)) and fail fast (throw an error)
to prevent recursing into a destination nested under the source. Additionally,
when iterating entries in copyDirRecursive (function copyDirRecursive), skip any
entry whose resolved source path equals or is inside resolvedDest to avoid
copying into the newly-created destination directory. Ensure references to src,
dest, opts.deleteFirst, and function name copyDirRecursive are used so the
checks are added in the right place.

Comment on lines +197 to +199
if (opts.deleteFirst) {
await fs.promises.rm(dest, { recursive: true, force: true }).catch(() => undefined);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't swallow deleteFirst failures.

Line 198 ignores every fs.promises.rm error, so permission or I/O failures silently turn into a merge with stale destination contents. force: true already covers the missing-path case.

🧹 Proposed fix
     if (opts.deleteFirst) {
-        await fs.promises.rm(dest, { recursive: true, force: true }).catch(() => undefined);
+        await fs.promises.rm(dest, { recursive: true, force: 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.

Suggested change
if (opts.deleteFirst) {
await fs.promises.rm(dest, { recursive: true, force: true }).catch(() => undefined);
}
if (opts.deleteFirst) {
await fs.promises.rm(dest, { recursive: true, force: true });
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/honkit/src/utils/fs.ts` around lines 197 - 199, The current
delete-first branch swallows all errors by appending .catch(() => undefined) to
fs.promises.rm(dest, { recursive: true, force: true }), which hides
permission/I/O failures; remove the catch and let the promise reject so callers
see real errors (i.e., replace await fs.promises.rm(dest, { recursive: true,
force: true }).catch(() => undefined) with a plain await fs.promises.rm(dest, {
recursive: true, force: true }) so opts.deleteFirst, fs.promises.rm and dest are
used but failures are not silently ignored).

Comment thread reports/README.md

| Report | Command |
| --- | --- |
| Fresh install log (wiped `node_modules`, regenerated lockfile) | `pnpm run report:install` → `install.log` |
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Correct output path in the report table.

The table says install.log, but the script writes to reports/install.log. Please align this row to avoid confusion.

Suggested doc fix
-| Fresh install log (wiped `node_modules`, regenerated lockfile) | `pnpm run report:install` → `install.log` |
+| Fresh install log (wiped `node_modules`, regenerated lockfile) | `pnpm run report:install` → `reports/install.log` |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| Fresh install log (wiped `node_modules`, regenerated lockfile) | `pnpm run report:install``install.log` |
| Fresh install log (wiped `node_modules`, regenerated lockfile) | `pnpm run report:install``reports/install.log` |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@reports/README.md` at line 6, Update the table row that starts with "Fresh
install log (wiped `node_modules`, regenerated lockfile)" so the output path
cell shows `reports/install.log` instead of `install.log` to match the actual
script output; locate and edit the Markdown table row containing the text `Fresh
install log` and replace the backticked `install.log` with
`reports/install.log`.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants