Skip to content

[change] Reduce library size #392#425

Merged
nemesifier merged 19 commits intomasterfrom
fix/issue-392
Feb 6, 2026
Merged

[change] Reduce library size #392#425
nemesifier merged 19 commits intomasterfrom
fix/issue-392

Conversation

@codesankalp
Copy link
Copy Markdown
Member

@codesankalp codesankalp commented Aug 27, 2025

Checklist

  • I have read the OpenWISP Contributing Guidelines.
  • I have manually tested the changes proposed in this pull request.
  • I have written new test cases for new code and/or updated existing tests for changes to existing code.
  • I have updated the documentation.

Reference to Existing Issue

Closes #392

Description of Changes

Use chunk splitting, dynamic loading and removed unused examples to reduce library size.

#425 (comment)

Copy link
Copy Markdown
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

The main problem I see is that now yarn build produces multiple files, which doesn't make it easy for us to drop the resulting work in OpenWISP. Is this on purpose?

@codesankalp
Copy link
Copy Markdown
Member Author

The main problem I see is that now yarn build produces multiple files, which doesn't make it easy for us to drop the resulting work in OpenWISP. Is this on purpose?

Yes, there are several reasons for this approach:

  1. Independent library caching - Libraries like ECharts and Leaflet will cache separately, independent of the netjsongraph codebase
  2. Efficient updates - New releases or code updates won't require re-downloading unchanged libraries
  3. Parallel loading - Modern browsers support concurrent chunk downloads, significantly improving load times
  4. Enhanced performance - Overall application performance will improve
  5. Selective loading - Load only required dependencies (network topology needs only ECharts, while map features require Leaflet as well)

The contenthash in filenames will optimize CDN cache management when using cdn (if we are using cdn).

Integration Considerations:

  1. Since we maintain the codebase, updating it to support multiple JavaScript files would be ideal. However, if this isn't feasible, I can make the chunk build process configurable through environment variables.
  2. Else, we could create a netjsongraph-loader.js file that handles parallel chunk loading, requiring only a single script reference in the integration code.

let me know your thoughts!

@nemesifier nemesifier moved this to In progress in 25.09 Release Sep 4, 2025
Copy link
Copy Markdown
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

@codesankalp what do you think about the following?

  1. a result set providing the current output: all separate files
  2. a result set providing the library + echarts
  3. a result set providing the library + echarts + leaflet

Can you add basic explanation about the above?

@codesankalp codesankalp force-pushed the fix/issue-392 branch 4 times, most recently from 68ed674 to 17ffba3 Compare September 8, 2025 14:02
@codesankalp
Copy link
Copy Markdown
Member Author

New Buid Sizes:

  1. a result set providing the current output: all separate files -> 16.5kb + 324kb + 44.2kb
  2. a result set providing the library + echarts -> 323kb
  3. a result set providing the library + echarts + leaflet -> 366kb

@codesankalp codesankalp force-pushed the fix/issue-392 branch 2 times, most recently from e43988e to 5355c03 Compare September 8, 2025 15:53
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
@codesankalp codesankalp force-pushed the fix/issue-392 branch 2 times, most recently from 0c99ec8 to b4bbfa9 Compare September 8, 2025 17:23
Copy link
Copy Markdown
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

I have the following problem:

yarn install
yarn install v1.22.22
[1/4] Resolving packages...
[2/4] Fetching packages...
error Error: https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.3.tgz: Request failed "404 Not Found"
    at ResponseError.ExtendableBuiltin (/usr/share/yarn/lib/cli.js:696:66)
    at new ResponseError (/usr/share/yarn/lib/cli.js:802:124)
    at Request.<anonymous> (/usr/share/yarn/lib/cli.js:66750:16)
    at Request.emit (node:events:519:28)
    at module.exports.Request.onRequestResponse (/usr/share/yarn/lib/cli.js:142287:10)
    at ClientRequest.emit (node:events:519:28)
    at HTTPParser.parserOnIncomingClient (node:_http_client:716:27)
    at HTTPParser.parserOnHeadersComplete (node:_http_common:117:17)
    at TLSSocket.socketOnData (node:_http_client:558:22)
    at TLSSocket.emit (node:events:519:28)
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.

I see the following line in yarn.lock:

debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.4.1:
  version "4.4.2"
  resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.2.tgz#96b480a7fe47cc04fe57f0bf56e058dba2a001fa"
  integrity sha512-IQeXCZhGRpFiLI3MYlCGLjNssUBiE8G21RMyNH35KFsxIvhrMeh5jXuG82woDZrYX9pgqHs+GF5js2Ducn4y4A==
  dependencies:
    ms "^2.1.3"

The URL https://registry.yarnpkg.com/debug/-/debug-4.4.2.tgz#96b480a7fe47cc04fe57f0bf56e058dba2a001fa returns 404.

I am not sure why the CI build is not complaining about this, but it feels wrong to me to reference a 404 URL in yarn.lock, what do you think?

PS: debug-js/debug#1005

Copy link
Copy Markdown
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

I did some changes to the README.

The save on the complete bundle is only ~300KB.
In OpenWISP dashboard we need to load the complete bundle, so reducing 300KB is good but I wonder if we really need 1.3 MB to show geographic and indoor maps.

Is there a way to reduce the imports of echarts so that we only import exactly what we use and let the javascript pruning tools do the work of removing all the JS code that we don't use?

Comment thread package.json Outdated
Comment thread .prettierrc Outdated
@codesankalp codesankalp force-pushed the fix/issue-392 branch 2 times, most recently from bc058f2 to e6c4b28 Compare September 10, 2025 22:04
@nemesifier nemesifier moved this from In progress to Backlog in 25.09 Release Sep 30, 2025
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

🤖 Fix all issues with AI agents
In `@package.json`:
- Around line 5-7: The package.json currently points "main" to
dist/netjsongraph.min.js but builds are ignored by git and not included in the
published package; add a "prepare" script (e.g., add "prepare": "yarn
build:full" to the scripts object so npm/yarn runs the build on publish) and/or
add a "files" array (e.g., include "dist/") to package.json to ensure the dist/
directory is packaged; update the scripts and/or files keys accordingly so that
the "main" entry exists in the published package.

In `@src/js/netjsongraph.config.js`:
- Around line 366-375: The crs getter on config calls getLeaflet() without
suppressing warnings, causing repeated console warnings in echarts-only builds;
update the getter to call getLeaflet(true) so it suppresses the warning when
Leaflet is absent and keep the rest of the logic (return L ? L.CRS.EPSG3857 :
null) intact—modify the Object.defineProperty for config.crs that currently
references getLeaflet() to use getLeaflet(true) instead.

In `@webpack-plugins/inject-leaflet-loader.js`:
- Around line 62-70: The onerror handler for the created script element
(leafletJS) only logs to console and can leave the page stuck; update
leafletJS.onerror to both surface a visible error and recover: create or update
a DOM error element (e.g., show a banner/message) and then call initMap() (or
loadLeafletPlugins if hasPlugins is true) so the page proceeds in a degraded
state; locate the leafletJS creation block and modify leafletJS.onerror to
display an in-page error message and invoke the appropriate initializer (initMap
or loadLeafletPlugins) instead of only console.error.

In `@webpack.config.js`:
- Around line 207-211: The webpack performance config currently sets
maxEntrypointSize and maxAssetSize too low for the full build; update the
performance block (performance, maxEntrypointSize, maxAssetSize) to either raise
maxAssetSize (and optionally maxEntrypointSize) to values that cover the full
"Library + ECharts + Leaflet" bundle (e.g., bump maxAssetSize to ~450000 or
similar) or add a comment documenting that production builds will generate
warnings for the full-build entrypoint and those warnings are expected; change
the numeric thresholds in the performance config or add the explanatory comment
accordingly.
🧹 Nitpick comments (4)
src/js/netjsongraph.core.js (1)

80-80: async render() never awaits the data-loading promise chain — consider returning or awaiting it.

The method is marked async but the paginatedDataParse .then() chain (Line 105) is fire-and-forget. This means await someInstance.render() resolves immediately, before data is loaded and the view is painted. If callers rely on the event system (onReady/onLoad) this works, but the async signature is misleading — it suggests awaiting it guarantees a rendered state.

Consider either:

  1. return this.utils.paginatedDataParse.call(…).then(…) so the caller can actually await completion, or
  2. Documenting clearly that render is fire-and-forget and callers should listen for lifecycle events.
src/js/leaflet-loader.js (1)

1-37: Clean runtime loader pattern — well structured.

The caching, build-time branching via BUNDLE_LEAFLET, and graceful fallback for the echarts-only build are solid. One tiny nit:

Line 1's /* eslint-disable import/no-mutable-exports */ appears unnecessary — the only export is the getLeaflet function declaration (an immutable binding); the mutable let L is module-private and not exported.

src/js/echarts-leaflet/LeafletCoordSys.js (1)

151-155: Dead code: typeof L === "undefined" can never be true here.

L is a closure variable captured from line 12. Since createLeafletCoordSystem() throws on line 14 if L is falsy, by the time LeafletCoordSys.create executes, L is guaranteed to be defined and truthy. This guard was meaningful when L was a global, but is now dead code.

♻️ Suggested cleanup
       if (leafletCoordSys) {
         throw new Error("Only one leaflet component can exist");
       }
-      if (typeof L === "undefined") {
-        throw new Error("Leaflet api is not loaded");
-      }
       if (!leafletModel.__map) {
webpack.config.js (1)

186-202: Consider extracting zlib constants to reduce repetition.

require("zlib") is called four times inline within the Brotli compression options. Extracting it to a local constant improves readability.

♻️ Suggested refactor
+const zlib = require("zlib");
+
 // ...inside the production plugins array:
             new CompressionPlugin({
               filename: "[path][base].br",
               algorithm: "brotliCompress",
               test: /\.(js|css|html|svg|json)$/,
               compressionOptions: {
                 params: {
-                  [require("zlib").constants.BROTLI_PARAM_QUALITY]: 11,
-                  [require("zlib").constants.BROTLI_PARAM_SIZE_HINT]: 0,
-                  [require("zlib").constants.BROTLI_PARAM_MODE]:
-                    require("zlib").constants.BROTLI_MODE_TEXT,
-                  [require("zlib").constants.BROTLI_PARAM_LGWIN]: 22,
-                  [require("zlib").constants.BROTLI_PARAM_LGBLOCK]: 0,
+                  [zlib.constants.BROTLI_PARAM_QUALITY]: 11,
+                  [zlib.constants.BROTLI_PARAM_SIZE_HINT]: 0,
+                  [zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT,
+                  [zlib.constants.BROTLI_PARAM_LGWIN]: 22,
+                  [zlib.constants.BROTLI_PARAM_LGBLOCK]: 0,
                 },
               },
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d8082d5 and 7af27bb.

⛔ Files ignored due to path filters (3)
  • lib/css/assets/leaflet-measure.png is excluded by !**/*.png
  • public/assets/images/floorplan.png is excluded by !**/*.png
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (27)
  • .eslintrc.js
  • .github/workflows/ci.yml
  • README.md
  • index.html
  • knip.json
  • package.json
  • public/assets/data/airplaneRouteMap.json
  • public/assets/data/netjsongraph-graphGL.json
  • public/example_templates/netjsongraph-graphGL.html
  • public/example_templates/netjsonmap-animation.html
  • public/example_templates/netjsonmap-indoormap.html
  • src/js/echarts-leaflet/LeafletCoordSys.js
  • src/js/echarts-leaflet/LeafletModel.js
  • src/js/echarts-leaflet/LeafletView.js
  • src/js/echarts-leaflet/index.js
  • src/js/leaflet-loader.js
  • src/js/netjsongraph.clients.js
  • src/js/netjsongraph.config.js
  • src/js/netjsongraph.core.js
  • src/js/netjsongraph.geojson.js
  • src/js/netjsongraph.js
  • src/js/netjsongraph.render.js
  • test/browser.test.utils.js
  • test/netjsongraph.duplicateNodes.test.js
  • test/netjsongraph.render.test.js
  • webpack-plugins/inject-leaflet-loader.js
  • webpack.config.js
💤 Files with no reviewable changes (4)
  • test/browser.test.utils.js
  • public/example_templates/netjsongraph-graphGL.html
  • public/assets/data/airplaneRouteMap.json
  • public/example_templates/netjsonmap-animation.html
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/js/netjsongraph.clients.js
  • knip.json
  • .eslintrc.js
  • test/netjsongraph.duplicateNodes.test.js
  • README.md
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,ts,css,scss,json}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using openwisp-qa-format command with Python virtualenv enabled and yarn lint:fix (runs eslint --fix and prettier via lint-staged)

Files:

  • webpack.config.js
  • package.json
  • webpack-plugins/inject-leaflet-loader.js
  • src/js/netjsongraph.js
  • src/js/echarts-leaflet/LeafletCoordSys.js
  • src/js/netjsongraph.geojson.js
  • src/js/netjsongraph.render.js
  • src/js/echarts-leaflet/LeafletModel.js
  • src/js/echarts-leaflet/LeafletView.js
  • src/js/echarts-leaflet/index.js
  • src/js/netjsongraph.config.js
  • test/netjsongraph.render.test.js
  • src/js/leaflet-loader.js
  • src/js/netjsongraph.core.js
src/**/*.js

📄 CodeRabbit inference engine (AGENTS.md)

Husky pre-commit hooks automatically run lint-staged to format JavaScript files in src/**/*.js with prettier

Files:

  • src/js/netjsongraph.js
  • src/js/echarts-leaflet/LeafletCoordSys.js
  • src/js/netjsongraph.geojson.js
  • src/js/netjsongraph.render.js
  • src/js/echarts-leaflet/LeafletModel.js
  • src/js/echarts-leaflet/LeafletView.js
  • src/js/echarts-leaflet/index.js
  • src/js/netjsongraph.config.js
  • src/js/leaflet-loader.js
  • src/js/netjsongraph.core.js
**/*.test.{js,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Write unit tests using Jest with jsdom and run with yarn test

Files:

  • test/netjsongraph.render.test.js
🧠 Learnings (11)
📓 Common learnings
Learnt from: codesankalp
Repo: openwisp/netjsongraph.js PR: 425
File: src/js/netjsongraph.render.js:1-26
Timestamp: 2026-01-20T16:51:06.213Z
Learning: In the netjsongraph.js project, imports from `echarts/lib/chart/*/install`, `echarts/lib/component/*/install`, and `echarts/lib/renderer/install*` paths produce better tree-shaking results than the public entry points (`echarts/charts`, `echarts/components`, `echarts/renderers`) in their webpack configuration. The project maintainer has tested both approaches and confirmed the `/lib/*/install` pattern yields smaller bundles.
📚 Learning: 2026-01-20T16:51:06.213Z
Learnt from: codesankalp
Repo: openwisp/netjsongraph.js PR: 425
File: src/js/netjsongraph.render.js:1-26
Timestamp: 2026-01-20T16:51:06.213Z
Learning: In the netjsongraph.js project, imports from `echarts/lib/chart/*/install`, `echarts/lib/component/*/install`, and `echarts/lib/renderer/install*` paths produce better tree-shaking results than the public entry points (`echarts/charts`, `echarts/components`, `echarts/renderers`) in their webpack configuration. The project maintainer has tested both approaches and confirmed the `/lib/*/install` pattern yields smaller bundles.

Applied to files:

  • webpack.config.js
  • package.json
  • webpack-plugins/inject-leaflet-loader.js
  • test/netjsongraph.render.test.js
📚 Learning: 2026-01-22T22:37:11.271Z
Learnt from: CR
Repo: openwisp/netjsongraph.js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-22T22:37:11.271Z
Learning: Applies to test/netjsongraph.browser.test.js : Write browser tests in test/netjsongraph.browser.test.js using Chrome/ChromeDriver and run with `yarn test test/netjsongraph.browser.test.js`

Applied to files:

  • package.json
  • test/netjsongraph.render.test.js
  • .github/workflows/ci.yml
📚 Learning: 2026-01-22T22:37:11.271Z
Learnt from: CR
Repo: openwisp/netjsongraph.js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-22T22:37:11.271Z
Learning: Applies to **/*.test.{js,ts} : Write unit tests using Jest with jsdom and run with `yarn test`

Applied to files:

  • package.json
  • .github/workflows/ci.yml
📚 Learning: 2026-01-04T08:00:40.936Z
Learnt from: dee077
Repo: openwisp/netjsongraph.js PR: 417
File: src/js/netjsongraph.util.js:1302-1341
Timestamp: 2026-01-04T08:00:40.936Z
Learning: In the netjsongraph.js codebase, the `== null` pattern is intentionally used to check for both null and undefined values in a single comparison, which is a well-established JavaScript idiom and should not be flagged for replacement with strict equality checks.

Applied to files:

  • webpack-plugins/inject-leaflet-loader.js
  • src/js/netjsongraph.render.js
📚 Learning: 2026-01-20T00:42:32.369Z
Learnt from: dee077
Repo: openwisp/netjsongraph.js PR: 417
File: src/js/netjsongraph.core.js:132-145
Timestamp: 2026-01-20T00:42:32.369Z
Learning: In the netjsongraph.js codebase, node IDs are guaranteed not to contain the "~" character, so using "source~target" as a composite key for links in nodeLinkIndex will not collide with node ID keys.

Applied to files:

  • webpack-plugins/inject-leaflet-loader.js
📚 Learning: 2026-01-20T16:50:56.414Z
Learnt from: codesankalp
Repo: openwisp/netjsongraph.js PR: 425
File: src/js/netjsongraph.render.js:1-26
Timestamp: 2026-01-20T16:50:56.414Z
Learning: In netjsongraph.js, prefer importing from echarts/lib/chart/*/install, echarts/lib/component/*/install, and echarts/lib/renderer/install* rather than the public entry points (echarts/charts, echarts/components, echarts/renderers) to improve tree-shaking and reduce bundle size. Apply this pattern across JS files in src (update imports accordingly) and verify by comparing bundle sizes between the install-path imports and the public-entry imports.

Applied to files:

  • src/js/netjsongraph.js
  • src/js/echarts-leaflet/LeafletCoordSys.js
  • src/js/netjsongraph.geojson.js
  • src/js/netjsongraph.render.js
  • src/js/echarts-leaflet/LeafletModel.js
  • src/js/echarts-leaflet/LeafletView.js
  • src/js/echarts-leaflet/index.js
  • src/js/netjsongraph.config.js
  • src/js/leaflet-loader.js
  • src/js/netjsongraph.core.js
📚 Learning: 2026-01-27T19:09:23.929Z
Learnt from: nemesifier
Repo: openwisp/netjsongraph.js PR: 473
File: src/js/netjsongraph.render.js:433-468
Timestamp: 2026-01-27T19:09:23.929Z
Learning: In the netjsongraph.js project, when implementing wheel zoom propagation from the graph container to the canvas: ECharts intercepts wheel events directly on the canvas with preventDefault(), so those events don't bubble to the container. A wheel listener on the container only catches events that occur outside the canvas (in the empty container space), making checks for e.target === canvas unnecessary since canvas events never reach the container listener.

Applied to files:

  • src/js/netjsongraph.js
  • src/js/echarts-leaflet/LeafletCoordSys.js
  • src/js/netjsongraph.render.js
📚 Learning: 2026-01-20T00:56:36.062Z
Learnt from: dee077
Repo: openwisp/netjsongraph.js PR: 417
File: src/js/netjsongraph.core.js:132-145
Timestamp: 2026-01-20T00:56:36.062Z
Learning: In the netjsongraph.js codebase, the data model does not support parallel links (multiple links between the same source and target). Ensure nodeLinkIndex is keyed by a single 'source~target' pair and that adding a link does not overwrite existing links for that pair. If parallel links are ever required, the data model must be changed (e.g., allow an array of links per 'source~target' key) rather than storing only one link.

Applied to files:

  • src/js/netjsongraph.geojson.js
  • src/js/netjsongraph.render.js
  • src/js/netjsongraph.config.js
  • test/netjsongraph.render.test.js
  • src/js/netjsongraph.core.js
📚 Learning: 2026-01-30T08:31:13.746Z
Learnt from: Dhruv-ub
Repo: openwisp/netjsongraph.js PR: 474
File: src/js/netjsongraph.render.js:347-351
Timestamp: 2026-01-30T08:31:13.746Z
Learning: In src/js/netjsongraph.render.js, treat showMapLabelsAtZoom = 0 as the explicit 'disabled' state (not a valid threshold). Do not rely on generic falsy checks for this flag. Update logic to distinguish between undefined/null (not set), 0 (disabled), and >0 (enabled at a given zoom). For example, use explicit checks such as: if (showMapLabelsAtZoom === 0) { /* labels hidden */ } else if (typeof showMapLabelsAtZoom === 'number' && showMapLabelsAtZoom > 0) { /* show labels at/above this zoom */ } else { /* default behavior or undefined */ }. If needed, document this convention or normalize input (e.g., set undefined to a sensible default) to avoid ambiguity in future reviews.

Applied to files:

  • src/js/netjsongraph.render.js
📚 Learning: 2026-01-22T22:37:11.271Z
Learnt from: CR
Repo: openwisp/netjsongraph.js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-22T22:37:11.271Z
Learning: Run QA checks using `./run-qa-checks` before committing code

Applied to files:

  • .github/workflows/ci.yml
🧬 Code graph analysis (7)
webpack.config.js (1)
webpack-plugins/inject-leaflet-loader.js (1)
  • path (5-5)
src/js/echarts-leaflet/LeafletCoordSys.js (2)
src/js/leaflet-loader.js (2)
  • L (12-12)
  • getLeaflet (14-37)
src/js/echarts-leaflet/index.js (1)
  • L (11-11)
src/js/netjsongraph.geojson.js (4)
src/js/leaflet-loader.js (2)
  • L (12-12)
  • getLeaflet (14-37)
src/js/echarts-leaflet/index.js (1)
  • L (11-11)
src/js/echarts-leaflet/LeafletCoordSys.js (1)
  • L (12-12)
src/js/netjsongraph.config.js (1)
  • L (370-370)
src/js/echarts-leaflet/LeafletView.js (1)
src/js/echarts-leaflet/LeafletCoordSys.js (3)
  • mapOffset (126-126)
  • mapOffset (132-132)
  • api (113-113)
src/js/netjsongraph.config.js (4)
src/js/leaflet-loader.js (2)
  • L (12-12)
  • getLeaflet (14-37)
src/js/echarts-leaflet/index.js (1)
  • L (11-11)
src/js/echarts-leaflet/LeafletCoordSys.js (1)
  • L (12-12)
src/js/netjsongraph.geojson.js (2)
  • L (136-136)
  • L (140-140)
test/netjsongraph.render.test.js (1)
test/netjsongraph.duplicateNodes.test.js (1)
  • NetJSONGraphRender (2-2)
src/js/leaflet-loader.js (4)
src/js/echarts-leaflet/index.js (1)
  • L (11-11)
src/js/echarts-leaflet/LeafletCoordSys.js (1)
  • L (12-12)
src/js/netjsongraph.config.js (1)
  • L (370-370)
src/js/netjsongraph.geojson.js (2)
  • L (136-136)
  • L (140-140)
🪛 Biome (2.3.13)
webpack-plugins/inject-leaflet-loader.js

[error] 160-160: Do not shadow the global "constructor" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

🪛 GitHub Check: CodeQL
.github/workflows/ci.yml

[warning] 52-129: Workflow does not contain permissions
Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}

⏰ 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: Tests and Coverage (dev)
🔇 Additional comments (28)
.github/workflows/ci.yml (1)

83-97: CI matrix covers all build variants with browser tests — good setup.

Conditional build/server steps cleanly separate the three paths, and browser tests (Line 121) run unconditionally across all matrix entries, which matches the reviewer request to exercise echarts-only builds in CI.

src/js/echarts-leaflet/LeafletView.js (1)

1-7: Clean refactor to direct echarts/core imports and parameterless factory.

The shift from dependency-injected echarts/L parameters to direct imports from echarts/core is consistent across all echarts-leaflet modules and removes unnecessary coupling. The const tightening on parts and mapOffset is a nice touch.

src/js/echarts-leaflet/LeafletModel.js (1)

1-7: Consistent refactor mirroring the LeafletView.js pattern — LGTM.

Direct import of extendComponentModel from echarts/core and parameterless factory signature align with the other echarts-leaflet module changes.

Also applies to: 17-17

src/js/netjsongraph.render.js (4)

1-26: Modular echarts imports via /lib/*/install paths — consistent with project learnings.

The use() registration pattern with individual install imports aligns with the tested approach that yields smaller bundles in this project's webpack configuration. Addition of CanvasRenderer alongside SVGRenderer ensures both renderers are available. Based on learnings, the /lib/*/install pattern produces better tree-shaking results than the public entry points in this project's webpack configuration.


515-520: Runtime Leaflet resolution with fail-fast error — solid approach.

Destructuring {circleMarker, latLngBounds} from the loaded Leaflet instance at the top of mapRender keeps the rest of the method clean and avoids repeated L. references. The early throw ensures a clear error message when Leaflet is genuinely missing.


206-207: Simplified to always use type: "graph" — completes the graphGL removal.

Hardcoding type: "graph" with a "force" layout fallback removes the last graphGL code path. The || "force" default is a safe choice for the standard graph layout.


871-871: Narrowed public API to a single default export.

Removing the named echarts and L exports in favor of export default NetJSONGraphRender is a clean API surface reduction. Consumers that need echarts or Leaflet directly should use echarts/core or the leaflet-loader respectively.

webpack-plugins/inject-leaflet-loader.js (1)

1-21: Build-time integrity computation looks good.

The approach of reading the Leaflet version and computing SRI hashes from the local node_modules copy at build time is solid — it ensures the CDN-served assets match what was tested locally.

public/example_templates/netjsonmap-indoormap.html (1)

13-17: LGTM — Leaflet JS CDN tag added alongside existing CSS.

The added script tag correctly matches the existing Leaflet CSS version (1.8.0) and includes an SRI integrity hash. The InjectLeafletLoaderPlugin will strip these static references during echarts-only builds and replace them with the dynamic loader, while full builds will override window.L with the bundled copy.

src/js/netjsongraph.geojson.js (1)

1-1: Clean migration to runtime Leaflet loading.

The pattern is consistent with other modules: getLeaflet() → early return if unavailable → destructure needed API. The geoJSON factory destructuring from L is correct for Leaflet 1.8.0.

Also applies to: 136-140, 167-167

test/netjsongraph.render.test.js (3)

1-3: Import changes align with the new default-export pattern.

L is now imported directly from "leaflet" and NetJSONGraphRender uses a default import, consistent with the refactored module exports.


477-481: Async render test properly awaits the now-async render().

The switch from a synchronous throw expectation to await map.render() + console.error assertion correctly reflects the new async render path.


707-715: Good addition: prototype isolation prevents test pollution.

The beforeEach/afterEach snapshot-and-restore of NetJSONGraphRender.prototype's prototype chain prevents the Object.setPrototypeOf call in setUp (line 718) from leaking across tests.

package.json (1)

90-101: Dependency restructuring looks correct.

Leaflet as an optional peer dependency, echarts and kdbush as direct dependencies — this properly supports both build variants. zrender removal is fine since it's a transitive dependency of echarts.

src/js/netjsongraph.js (2)

8-11: Conditional Leaflet bundling for full builds.

The BUNDLE_LEAFLET guard correctly ensures Leaflet is only required and assigned to window.L in the full build variant. This runs before any component initialization, so getLeaflet() calls in downstream modules will find it.


176-178: Default export added alongside window.NetJSONGraph.

This properly supports both ESM bundler consumers (via import) and script-tag consumers (via window.NetJSONGraph).

src/js/echarts-leaflet/LeafletCoordSys.js (2)

11-24: Clean refactoring to loader-based Leaflet access.

The parameterless factory that obtains Leaflet via getLeaflet() and destructures the needed APIs is well-structured. The throw on line 14 ensures no code path proceeds without a valid Leaflet instance.


175-197: Destructured Leaflet API usage is correct.

lMap, lTileLayer, and control.layers properly replace the former L.map, L.tileLayer, and L.control.layers calls. The aliased names (lMap, lTileLayer) avoid conflicts with local variables.

src/js/netjsongraph.config.js (2)

1-1: LGTM on the import change.

Clean switch from direct Leaflet import to the runtime loader pattern, consistent with the rest of the codebase.


377-378: Named + default exports look good.

Exporting prepareData from the original config object (not the spread copy) preserves the original reference, and the default export is the enhanced config with the dynamic crs getter.

webpack.config.js (5)

22-59: Aggressive Terser settings look appropriate for a production library bundle.

The multi-pass compression with pure_getters, collapse_vars, and toplevel is well-suited for reducing the final output. hoist_vars: false avoids potential issues with variable hoisting across modules.


89-103: sideEffects: true is correctly set now—good.

This properly enables webpack's side-effects optimization, consistent with the past review feedback.


83-88: Leaflet externalization for echarts-only build is correctly configured.

Mapping leaflet to the global L ensures Leaflet is excluded from the bundle while remaining available at runtime via window.L.


169-171: DefinePlugin for BUNDLE_LEAFLET correctly drives the runtime loader.

JSON.stringify(!isEchartsOnly) ensures the flag is properly injected as a boolean literal, which the leaflet-loader.js module checks at runtime.


68-81: The review comment is incorrect. The build script in package.json is defined as "yarn build:full" only, not as a combined script that runs both build:full and build:echarts-only sequentially. The two build variants are separate scripts that must be invoked independently. While clean: true would erase the first build's output if both scripts were run one after another manually, this is not an automated workflow issue in the current package.json configuration.

Likely an incorrect or invalid review comment.

src/js/echarts-leaflet/index.js (3)

1-5: Imports are well-structured.

Using echarts/core for the registration APIs (registerCoordinateSystem, registerAction) is correct—the tree-shaking learning about echarts/lib/*/install paths applies to chart/component/renderer modules, not the core registration surface.


10-16: Clean early-exit for missing Leaflet.

getLeaflet(true) correctly suppresses the warning, and bailing out before any Leaflet-dependent registration ensures the echarts-only build works without errors. This is the right pattern.


18-36: Registration flow looks correct.

The parameterless calls to extendLeafletModel(), extendLeafletView(), and createLeafletCoordSystem() align with the refactored modules that now source their dependencies internally. The registerAction callback logic is unchanged.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread package.json
Comment thread src/js/netjsongraph.config.js
Comment thread webpack-plugins/inject-leaflet-loader.js
Comment thread webpack.config.js
@codesankalp
Copy link
Copy Markdown
Member Author

@codesankalp I've added a browser test for the leaflet plugin example in #503 and proceeded to merge this branch with master to get the test run in the CI build here, which is now failing as expected, do you see the JS console error now?

I thought you were facing issues in echarts build, and i was testing that only. After making the change, all the tests are passing.

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

🤖 Fix all issues with AI agents
In `@package.json`:
- Around line 5-10: The package.json exposes "module": "src/js/netjsongraph.js"
but the "files" array only publishes "dist/", so consumers cannot resolve the
module entry; fix by either adding the source path to the published files (add
"src/" or "src/js/netjsongraph.js" to the "files" array) so the module field
remains valid, or remove/update the "module" field to point to a file inside
"dist/" (e.g., the bundled ES module) if only pre-built artifacts are intended
to be published.
🧹 Nitpick comments (8)
src/js/netjsongraph.core.js (3)

77-83: async keyword is unnecessary — no top-level await in the method body.

render() is marked async but never uses await in its own scope. All asynchronous work is kicked off via .then() chains (line 108) and event.once callbacks. The async keyword only wraps the return value in a Promise<undefined> that resolves immediately — before any data is loaded or rendered — which is inconsistent with what callers might expect from an async method.

The JSDoc correctly says "fire-and-forget," but if callers genuinely should not await this, dropping async makes that contract explicit and avoids a misleading return type.

Suggested fix
-  async render() {
+  render() {

100-107: Dead catch block — onReadyDone never rejects.

onReadyDone (lines 89-97) always calls resolve() unconditionally, even when the onReady callback throws (the error is logged, then resolve() runs). That means await onReadyDone on line 102 can never reject, so the catch on lines 103-105 is dead code.

Either remove the dead catch, or change the onReadyDone promise to actually reject on error so the catch serves a purpose.

Option A — remove dead catch
     this.event.once("applyUrlFragmentState", async () => {
-      try {
-        await onReadyDone;
-      } catch (e) {
-        console.error("onReady failed:", e);
-      }
+      await onReadyDone;
       this.utils.applyUrlFragmentState.call(this, this);
     });

89-97: onReady errors are silently swallowed — consider propagating.

When this.config.onReady throws, the error is logged (line 94), but resolve() is still called, so downstream code (e.g. applyUrlFragmentState) proceeds as if nothing went wrong. This may be intentional for resilience, but it means a broken onReady hook will silently degrade the application state.

If this is deliberate, a brief inline comment explaining why errors are swallowed (rather than rejecting) would help future maintainers.

webpack-plugins/inject-leaflet-loader.js (3)

9-21: Module-level I/O reads Leaflet files at require-time — acceptable but fragile.

The integrity hash computation reads from node_modules/leaflet/dist/ when the plugin is first required. This will throw with a clear error if Leaflet isn't installed (e.g., missing devDependency). Since Leaflet is now a devDependency, this should always be available during builds.

Consider wrapping the file reads in a try/catch with a descriptive error message to fail gracefully.


80-96: waitForLeaflet polls even on confirmed CDN failure — 5s delay before proceeding.

When leafletJS.onerror fires (line 72), loadLeafletPlugins() is called, which enters waitForLeaflet and polls for L up to 50 × 100ms = 5 seconds. Since the CDN load already failed, L will never appear, so this always burns through the full timeout before calling initMap().

Consider passing a flag or skipping the poll when the error path is known:

♻️ Sketch — skip polling on known failure
         leafletJS.onerror = function() {
           console.error('Failed to load Leaflet from CDN. Please check your internet connection.');
           const errorDiv = document.createElement('div');
           errorDiv.style.cssText = 'position:fixed;top:0;left:0;right:0;background:`#f44336`;color:white;padding:10px;text-align:center;z-index:9999;';
           errorDiv.textContent = 'Error: Failed to load Leaflet. Please check your internet connection.';
           document.body.appendChild(errorDiv);
-          ${hasPlugins ? "loadLeafletPlugins();" : "initMap();"}
+          initMap();
         };

When Leaflet fails to load, calling loadLeafletPlugins() (which waits for L) adds an unnecessary 5-second delay. Calling initMap() directly allows the page to render in its degraded state immediately.


201-207: Remove the do/while loop — String.replace() with the global regex flag replaces all matches in a single call.

The g flag in leafletJSRegex causes replace() to match and remove all occurrences at once. The loop executes twice (once to remove all matches, once to verify nothing changed), making it redundant. The CSS removal on line ~199 correctly omits this pattern.

♻️ Suggested simplification
     // Remove main leaflet.js script if present
     const leafletJSRegex = /<script[^>]*leaflet\.js[^>]*>[\s\S]*?<\/script>/gi;
-    let prev;
-    do {
-      prev = html;
-      html = html.replace(leafletJSRegex, "");
-    } while (html !== prev);
+    html = html.replace(leafletJSRegex, "");
webpack.config.js (2)

69-82: clean: true will wipe the other variant's output if both builds are run in sequence.

If a user runs yarn build:full followed by yarn build:echarts-only (or vice versa), the second build deletes the first build's output because both target dist/ with clean: true. This is likely the intended behavior (one variant at a time), but worth documenting or noting.

If both outputs should coexist, clean: true would need to be false or scoped to specific filenames.


142-206: Plugin composition is well structured.

Good use of conditional spreads for production-only compression and optional bundle analysis. The DefinePlugin for BUNDLE_LEAFLET correctly drives the runtime Leaflet loading behavior in leaflet-loader.js.

One minor note: BundleAnalyzerPlugin (line 9) is eagerly require()-d at module load even when ANALYZE is unset. A lazy require would shave off startup time for normal builds.

♻️ Lazy require
-const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
 // ... later in plugins array:
-      ...(process.env.ANALYZE ? [new BundleAnalyzerPlugin()] : []),
+      ...(process.env.ANALYZE
+        ? [new (require("webpack-bundle-analyzer").BundleAnalyzerPlugin)()]
+        : []),
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7af27bb and 7446060.

📒 Files selected for processing (7)
  • package.json
  • src/js/echarts-leaflet/LeafletCoordSys.js
  • src/js/leaflet-loader.js
  • src/js/netjsongraph.config.js
  • src/js/netjsongraph.core.js
  • webpack-plugins/inject-leaflet-loader.js
  • webpack.config.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/js/leaflet-loader.js
  • src/js/echarts-leaflet/LeafletCoordSys.js
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,ts,css,scss,json}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using openwisp-qa-format command with Python virtualenv enabled and yarn lint:fix (runs eslint --fix and prettier via lint-staged)

Files:

  • webpack.config.js
  • src/js/netjsongraph.config.js
  • package.json
  • src/js/netjsongraph.core.js
  • webpack-plugins/inject-leaflet-loader.js
src/**/*.js

📄 CodeRabbit inference engine (AGENTS.md)

Husky pre-commit hooks automatically run lint-staged to format JavaScript files in src/**/*.js with prettier

Files:

  • src/js/netjsongraph.config.js
  • src/js/netjsongraph.core.js
🧠 Learnings (9)
📓 Common learnings
Learnt from: codesankalp
Repo: openwisp/netjsongraph.js PR: 425
File: src/js/netjsongraph.render.js:1-26
Timestamp: 2026-01-20T16:51:06.213Z
Learning: In the netjsongraph.js project, imports from `echarts/lib/chart/*/install`, `echarts/lib/component/*/install`, and `echarts/lib/renderer/install*` paths produce better tree-shaking results than the public entry points (`echarts/charts`, `echarts/components`, `echarts/renderers`) in their webpack configuration. The project maintainer has tested both approaches and confirmed the `/lib/*/install` pattern yields smaller bundles.
📚 Learning: 2026-01-20T16:51:06.213Z
Learnt from: codesankalp
Repo: openwisp/netjsongraph.js PR: 425
File: src/js/netjsongraph.render.js:1-26
Timestamp: 2026-01-20T16:51:06.213Z
Learning: In the netjsongraph.js project, imports from `echarts/lib/chart/*/install`, `echarts/lib/component/*/install`, and `echarts/lib/renderer/install*` paths produce better tree-shaking results than the public entry points (`echarts/charts`, `echarts/components`, `echarts/renderers`) in their webpack configuration. The project maintainer has tested both approaches and confirmed the `/lib/*/install` pattern yields smaller bundles.

Applied to files:

  • webpack.config.js
  • package.json
  • webpack-plugins/inject-leaflet-loader.js
📚 Learning: 2026-01-20T16:50:56.414Z
Learnt from: codesankalp
Repo: openwisp/netjsongraph.js PR: 425
File: src/js/netjsongraph.render.js:1-26
Timestamp: 2026-01-20T16:50:56.414Z
Learning: In netjsongraph.js, prefer importing from echarts/lib/chart/*/install, echarts/lib/component/*/install, and echarts/lib/renderer/install* rather than the public entry points (echarts/charts, echarts/components, echarts/renderers) to improve tree-shaking and reduce bundle size. Apply this pattern across JS files in src (update imports accordingly) and verify by comparing bundle sizes between the install-path imports and the public-entry imports.

Applied to files:

  • src/js/netjsongraph.config.js
  • src/js/netjsongraph.core.js
📚 Learning: 2026-01-20T00:56:36.062Z
Learnt from: dee077
Repo: openwisp/netjsongraph.js PR: 417
File: src/js/netjsongraph.core.js:132-145
Timestamp: 2026-01-20T00:56:36.062Z
Learning: In the netjsongraph.js codebase, the data model does not support parallel links (multiple links between the same source and target). Ensure nodeLinkIndex is keyed by a single 'source~target' pair and that adding a link does not overwrite existing links for that pair. If parallel links are ever required, the data model must be changed (e.g., allow an array of links per 'source~target' key) rather than storing only one link.

Applied to files:

  • src/js/netjsongraph.config.js
  • src/js/netjsongraph.core.js
📚 Learning: 2026-01-22T22:37:11.271Z
Learnt from: CR
Repo: openwisp/netjsongraph.js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-22T22:37:11.271Z
Learning: Applies to test/netjsongraph.browser.test.js : Write browser tests in test/netjsongraph.browser.test.js using Chrome/ChromeDriver and run with `yarn test test/netjsongraph.browser.test.js`

Applied to files:

  • package.json
📚 Learning: 2026-01-22T22:37:11.271Z
Learnt from: CR
Repo: openwisp/netjsongraph.js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-22T22:37:11.271Z
Learning: Applies to **/*.test.{js,ts} : Write unit tests using Jest with jsdom and run with `yarn test`

Applied to files:

  • package.json
📚 Learning: 2026-01-22T22:37:11.271Z
Learnt from: CR
Repo: openwisp/netjsongraph.js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-22T22:37:11.271Z
Learning: Applies to **/*.{js,ts,css,scss,json} : Format code using `openwisp-qa-format` command with Python virtualenv enabled and `yarn lint:fix` (runs eslint --fix and prettier via lint-staged)

Applied to files:

  • package.json
📚 Learning: 2026-01-04T08:00:40.936Z
Learnt from: dee077
Repo: openwisp/netjsongraph.js PR: 417
File: src/js/netjsongraph.util.js:1302-1341
Timestamp: 2026-01-04T08:00:40.936Z
Learning: In the netjsongraph.js codebase, the `== null` pattern is intentionally used to check for both null and undefined values in a single comparison, which is a well-established JavaScript idiom and should not be flagged for replacement with strict equality checks.

Applied to files:

  • webpack-plugins/inject-leaflet-loader.js
📚 Learning: 2026-01-20T00:42:32.369Z
Learnt from: dee077
Repo: openwisp/netjsongraph.js PR: 417
File: src/js/netjsongraph.core.js:132-145
Timestamp: 2026-01-20T00:42:32.369Z
Learning: In the netjsongraph.js codebase, node IDs are guaranteed not to contain the "~" character, so using "source~target" as a composite key for links in nodeLinkIndex will not collide with node ID keys.

Applied to files:

  • webpack-plugins/inject-leaflet-loader.js
🧬 Code graph analysis (3)
webpack.config.js (1)
webpack-plugins/inject-leaflet-loader.js (1)
  • path (5-5)
src/js/netjsongraph.config.js (4)
src/js/echarts-leaflet/LeafletCoordSys.js (1)
  • L (12-12)
src/js/leaflet-loader.js (2)
  • L (11-11)
  • getLeaflet (13-36)
src/js/echarts-leaflet/index.js (1)
  • L (11-11)
src/js/netjsongraph.geojson.js (2)
  • L (136-136)
  • L (140-140)
webpack-plugins/inject-leaflet-loader.js (2)
webpack.config.js (2)
  • InjectLeafletLoaderPlugin (11-11)
  • HtmlWebpackPlugin (6-6)
test/browser.test.utils.js (1)
  • options (20-20)
🪛 Biome (2.3.13)
webpack-plugins/inject-leaflet-loader.js

[error] 165-165: Do not shadow the global "constructor" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

🔇 Additional comments (6)
src/js/netjsongraph.core.js (1)

14-30: Unchanged segments look correct.

The constructor, setConfig, data-loading pipeline (paginatedDataParse → node/link indexing → render), and setUtils are unchanged and consistent with the existing patterns and retrieved learnings (single source~target key for nodeLinkIndex, no parallel-link support).

Also applies to: 42-71, 108-182, 194-220

package.json (2)

15-17: Build scripts and dual build variants look good.

Clean separation of build:full and build:echarts-only with build defaulting to build:full. The --env BUILD_TYPE flag correctly propagates the build variant to webpack.


94-105: Leaflet dependency structure is correct.

Leaflet is properly in devDependencies (for local development/builds), peerDependencies (to signal consumers), and marked optional in peerDependenciesMeta. This allows echarts-only consumers to skip Leaflet entirely.

webpack-plugins/inject-leaflet-loader.js (1)

43-76: CDN error handling now shows a visible error — good improvement.

The onerror handler (lines 66-72) now appends a visible error banner and still calls initMap() / loadLeafletPlugins() to allow degraded functionality. This addresses the prior review concern about silent failures.

webpack.config.js (1)

62-104: Build variant logic and optimization settings look solid.

Clean separation of isProduction/isEchartsOnly flags, correct Leaflet externalization for echarts-only builds, and sideEffects: true properly enables tree-shaking. The TerserPlugin configuration is thorough with multi-pass compression.

src/js/netjsongraph.config.js (1)

366-378: Shallow copy is mitigated by deep copy on instantiation.

While {...NetJSONGraphDefaultConfig} creates a shallow copy where nested objects are shared by reference, this is not a concern in practice. Each NetJSONGraph instance performs a deep copy of the config in netjsongraph.core.js:24 (this.config = this.utils.deepCopy(NetJSONGraphDefaultConfig)), ensuring nested objects are isolated per instance. Mutations to one instance's config do not affect others.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread package.json
Comment thread webpack-plugins/inject-leaflet-loader.js Fixed
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: 1

🤖 Fix all issues with AI agents
In `@webpack-plugins/inject-leaflet-loader.js`:
- Around line 218-225: The current logic runs a fresh regex against the entire
html to compute scriptType (scriptTypeMatch / scriptType), which can pick up a
different <script> tag; instead, derive the type from the previously captured
script tag match (the variable produced by scriptRegex / match) rather than
running a new regex on html—use the existing capture group that holds the type
(if present) to set scriptType, falling back to "text/javascript" when that
capture is undefined.
🧹 Nitpick comments (2)
webpack-plugins/inject-leaflet-loader.js (2)

9-21: Module-level I/O runs unconditionally, even when the plugin is a no-op.

Lines 9–21 read and hash files from node_modules/leaflet at require()-time, before the constructor's isEchartsOnly check. If Leaflet isn't installed (e.g., treated as a peer dependency in some downstream setup), this crashes the entire build with a raw ENOENT, even for full builds where the plugin does nothing.

Consider deferring these reads into apply() (or a lazy getter) so the work only happens when isEchartsOnly is true.

♻️ Suggested approach — lazy initialization
-const leafletPkgPath = path.resolve(__dirname, "../node_modules/leaflet/package.json");
-const leafletPkg = JSON.parse(fs.readFileSync(leafletPkgPath, "utf8"));
-const LEAFLET_VERSION = leafletPkg.version;
-
-function getIntegrity(filePath) {
-  const fullPath = path.resolve(__dirname, "../node_modules/leaflet/dist", filePath);
-  const content = fs.readFileSync(fullPath);
-  const hash = crypto.createHash("sha512").update(content).digest("base64");
-  return `sha512-${hash}`;
-}
-
-const LEAFLET_CSS_INTEGRITY = getIntegrity("leaflet.css");
-const LEAFLET_JS_INTEGRITY = getIntegrity("leaflet.js");
+let _leafletMeta;
+function getLeafletMeta() {
+  if (!_leafletMeta) {
+    const leafletPkgPath = path.resolve(__dirname, "../node_modules/leaflet/package.json");
+    const leafletPkg = JSON.parse(fs.readFileSync(leafletPkgPath, "utf8"));
+    const version = leafletPkg.version;
+    const integrity = (file) => {
+      const fullPath = path.resolve(__dirname, "../node_modules/leaflet/dist", file);
+      const content = fs.readFileSync(fullPath);
+      const hash = crypto.createHash("sha512").update(content).digest("base64");
+      return `sha512-${hash}`;
+    };
+    _leafletMeta = {
+      version,
+      cssIntegrity: integrity("leaflet.css"),
+      jsIntegrity: integrity("leaflet.js"),
+    };
+  }
+  return _leafletMeta;
+}

Then use getLeafletMeta() inside getLeafletLoaderSnippet().


195-200: Redundant do-while loop — String.replace with /g already replaces all matches.

String.prototype.replace() with a global regex replaces every occurrence in a single call. The loop adds complexity for no benefit.

♻️ Simplification
       // Remove main leaflet.js script if present
       const leafletJSRegex = /<script[^>]*leaflet\.js[^>]*>[\s\S]*?<\/script>/gi;
-      let prev;
-      do {
-        prev = html;
-        html = html.replace(leafletJSRegex, "");
-      } while (html !== prev); // remove all occurrences
+      html = html.replace(leafletJSRegex, "");
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c810566 and 7695681.

📒 Files selected for processing (1)
  • webpack-plugins/inject-leaflet-loader.js
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,ts,css,scss,json}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using openwisp-qa-format command with Python virtualenv enabled and yarn lint:fix (runs eslint --fix and prettier via lint-staged)

Files:

  • webpack-plugins/inject-leaflet-loader.js
🧠 Learnings (6)
📓 Common learnings
Learnt from: codesankalp
Repo: openwisp/netjsongraph.js PR: 425
File: src/js/netjsongraph.render.js:1-26
Timestamp: 2026-01-20T16:51:06.213Z
Learning: In the netjsongraph.js project, imports from `echarts/lib/chart/*/install`, `echarts/lib/component/*/install`, and `echarts/lib/renderer/install*` paths produce better tree-shaking results than the public entry points (`echarts/charts`, `echarts/components`, `echarts/renderers`) in their webpack configuration. The project maintainer has tested both approaches and confirmed the `/lib/*/install` pattern yields smaller bundles.
📚 Learning: 2026-01-20T16:51:06.213Z
Learnt from: codesankalp
Repo: openwisp/netjsongraph.js PR: 425
File: src/js/netjsongraph.render.js:1-26
Timestamp: 2026-01-20T16:51:06.213Z
Learning: In the netjsongraph.js project, imports from `echarts/lib/chart/*/install`, `echarts/lib/component/*/install`, and `echarts/lib/renderer/install*` paths produce better tree-shaking results than the public entry points (`echarts/charts`, `echarts/components`, `echarts/renderers`) in their webpack configuration. The project maintainer has tested both approaches and confirmed the `/lib/*/install` pattern yields smaller bundles.

Applied to files:

  • webpack-plugins/inject-leaflet-loader.js
📚 Learning: 2026-01-04T08:00:40.936Z
Learnt from: dee077
Repo: openwisp/netjsongraph.js PR: 417
File: src/js/netjsongraph.util.js:1302-1341
Timestamp: 2026-01-04T08:00:40.936Z
Learning: In the netjsongraph.js codebase, the `== null` pattern is intentionally used to check for both null and undefined values in a single comparison, which is a well-established JavaScript idiom and should not be flagged for replacement with strict equality checks.

Applied to files:

  • webpack-plugins/inject-leaflet-loader.js
📚 Learning: 2026-01-20T00:42:32.369Z
Learnt from: dee077
Repo: openwisp/netjsongraph.js PR: 417
File: src/js/netjsongraph.core.js:132-145
Timestamp: 2026-01-20T00:42:32.369Z
Learning: In the netjsongraph.js codebase, node IDs are guaranteed not to contain the "~" character, so using "source~target" as a composite key for links in nodeLinkIndex will not collide with node ID keys.

Applied to files:

  • webpack-plugins/inject-leaflet-loader.js
📚 Learning: 2026-01-24T23:09:24.928Z
Learnt from: dee077
Repo: openwisp/netjsongraph.js PR: 444
File: src/js/netjsongraph.util.js:1404-1442
Timestamp: 2026-01-24T23:09:24.928Z
Learning: In the `moveNodeInRealTime` function in `src/js/netjsongraph.util.js`, it is recommended that `node.properties` be set, and the function should return early when `properties` is not provided rather than initializing it or handling it gracefully.

Applied to files:

  • webpack-plugins/inject-leaflet-loader.js
📚 Learning: 2026-01-20T00:56:47.084Z
Learnt from: dee077
Repo: openwisp/netjsongraph.js PR: 417
File: src/js/netjsongraph.core.js:132-145
Timestamp: 2026-01-20T00:56:47.084Z
Learning: In the netjsongraph.js codebase, the data model does not support parallel links (multiple links between the same pair of nodes), so storing a single link per "source~target" key in nodeLinkIndex is appropriate and won't overwrite any links.

Applied to files:

  • webpack-plugins/inject-leaflet-loader.js
🪛 Biome (2.3.13)
webpack-plugins/inject-leaflet-loader.js

[error] 165-165: Do not shadow the global "constructor" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

🔇 Additional comments (1)
webpack-plugins/inject-leaflet-loader.js (1)

43-155: Loader snippet generation is well-structured.

The CDN loader correctly handles: Leaflet-already-loaded fast path, integrity-checked resource injection, graceful degradation with a visible error banner on CDN failure, parallel plugin loading with proper coordination, and polling as a safety net. The initMap() function left open at line 154 (closed by the caller) is a code-generation pattern that works but is somewhat non-obvious — a brief comment at line 154 noting // closed by apply() would help future readers.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread webpack-plugins/inject-leaflet-loader.js Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@webpack-plugins/inject-leaflet-loader.js`:
- Around line 230-231: The regex used to extract the script type in the inject
logic is incorrect (it uses /type="([^"])"/i which only captures one char), so
typeMatch is always null and scriptType falls back to "text/javascript"; update
the pattern used where match and typeMatch are computed (the code that assigns
typeMatch from match[0]) to use a quantifier that captures one-or-more non-quote
characters (e.g., change to a pattern equivalent to /type="([^"]+)"/i), then
leave the scriptType assignment (scriptType = typeMatch ? typeMatch[1] :
"text/javascript") unchanged so real type attributes like "module" or
"text/javascript" are preserved.
- Around line 9-17: Rename the private variable _leafletMeta to leafletMeta
everywhere (declaration and all references, e.g., in the getLeafletMeta function
and any other uses) to remove the leading underscore and satisfy
no-underscore-dangle, and replace the property access of leafletPkg.version with
destructuring: const { version } = leafletPkg; ensuring any subsequent uses
reference the new version variable and the renamed leafletMeta.
🧹 Nitpick comments (1)
webpack-plugins/inject-leaflet-loader.js (1)

206-211: The do…while loop is redundant with a g-flag regex.

String.prototype.replace with a global (g) regex already replaces all matches in a single call. The loop will always exit after one iteration.

♻️ Simplification
         // Remove main leaflet.js script if present
         const leafletJSRegex = /<script[^>]*leaflet\.js[^>]*>[\s\S]*?<\/script>/gi;
-        let prev;
-        do {
-          prev = html;
-          html = html.replace(leafletJSRegex, "");
-        } while (html !== prev); // remove all occurrences
+        html = html.replace(leafletJSRegex, "");
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7695681 and 7ff15ec.

📒 Files selected for processing (1)
  • webpack-plugins/inject-leaflet-loader.js
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,ts,css,scss,json}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using openwisp-qa-format command with Python virtualenv enabled and yarn lint:fix (runs eslint --fix and prettier via lint-staged)

Files:

  • webpack-plugins/inject-leaflet-loader.js
🧠 Learnings (6)
📓 Common learnings
Learnt from: codesankalp
Repo: openwisp/netjsongraph.js PR: 425
File: src/js/netjsongraph.render.js:1-26
Timestamp: 2026-01-20T16:51:06.213Z
Learning: In the netjsongraph.js project, imports from `echarts/lib/chart/*/install`, `echarts/lib/component/*/install`, and `echarts/lib/renderer/install*` paths produce better tree-shaking results than the public entry points (`echarts/charts`, `echarts/components`, `echarts/renderers`) in their webpack configuration. The project maintainer has tested both approaches and confirmed the `/lib/*/install` pattern yields smaller bundles.
📚 Learning: 2026-01-20T16:51:06.213Z
Learnt from: codesankalp
Repo: openwisp/netjsongraph.js PR: 425
File: src/js/netjsongraph.render.js:1-26
Timestamp: 2026-01-20T16:51:06.213Z
Learning: In the netjsongraph.js project, imports from `echarts/lib/chart/*/install`, `echarts/lib/component/*/install`, and `echarts/lib/renderer/install*` paths produce better tree-shaking results than the public entry points (`echarts/charts`, `echarts/components`, `echarts/renderers`) in their webpack configuration. The project maintainer has tested both approaches and confirmed the `/lib/*/install` pattern yields smaller bundles.

Applied to files:

  • webpack-plugins/inject-leaflet-loader.js
📚 Learning: 2026-01-04T08:00:40.936Z
Learnt from: dee077
Repo: openwisp/netjsongraph.js PR: 417
File: src/js/netjsongraph.util.js:1302-1341
Timestamp: 2026-01-04T08:00:40.936Z
Learning: In the netjsongraph.js codebase, the `== null` pattern is intentionally used to check for both null and undefined values in a single comparison, which is a well-established JavaScript idiom and should not be flagged for replacement with strict equality checks.

Applied to files:

  • webpack-plugins/inject-leaflet-loader.js
📚 Learning: 2026-01-20T00:42:32.369Z
Learnt from: dee077
Repo: openwisp/netjsongraph.js PR: 417
File: src/js/netjsongraph.core.js:132-145
Timestamp: 2026-01-20T00:42:32.369Z
Learning: In the netjsongraph.js codebase, node IDs are guaranteed not to contain the "~" character, so using "source~target" as a composite key for links in nodeLinkIndex will not collide with node ID keys.

Applied to files:

  • webpack-plugins/inject-leaflet-loader.js
📚 Learning: 2026-01-24T23:09:24.928Z
Learnt from: dee077
Repo: openwisp/netjsongraph.js PR: 444
File: src/js/netjsongraph.util.js:1404-1442
Timestamp: 2026-01-24T23:09:24.928Z
Learning: In the `moveNodeInRealTime` function in `src/js/netjsongraph.util.js`, it is recommended that `node.properties` be set, and the function should return early when `properties` is not provided rather than initializing it or handling it gracefully.

Applied to files:

  • webpack-plugins/inject-leaflet-loader.js
📚 Learning: 2026-01-20T00:56:47.084Z
Learnt from: dee077
Repo: openwisp/netjsongraph.js PR: 417
File: src/js/netjsongraph.core.js:132-145
Timestamp: 2026-01-20T00:56:47.084Z
Learning: In the netjsongraph.js codebase, the data model does not support parallel links (multiple links between the same pair of nodes), so storing a single link per "source~target" key in nodeLinkIndex is appropriate and won't overwrite any links.

Applied to files:

  • webpack-plugins/inject-leaflet-loader.js
🧬 Code graph analysis (1)
webpack-plugins/inject-leaflet-loader.js (1)
webpack.config.js (3)
  • fs (2-2)
  • InjectLeafletLoaderPlugin (10-10)
  • HtmlWebpackPlugin (6-6)
🪛 Biome (2.3.13)
webpack-plugins/inject-leaflet-loader.js

[error] 176-176: Do not shadow the global "constructor" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

🪛 ESLint
webpack-plugins/inject-leaflet-loader.js

[error] 9-9: Unexpected dangling '_' in '_leafletMeta'.

(no-underscore-dangle)


[error] 17-17: Use object destructuring.

(prefer-destructuring)

🪛 GitHub Actions: netjsongraph.js CI BUILD
webpack-plugins/inject-leaflet-loader.js

[error] 9-9: ESLint: Unexpected dangling '_' in '_leafletMeta' (no-underscore-dangle).

🪛 GitHub Check: QA-Checks
webpack-plugins/inject-leaflet-loader.js

[failure] 17-17:
Use object destructuring


[failure] 9-9:
Unexpected dangling '_' in '_leafletMeta'

🔇 Additional comments (1)
webpack-plugins/inject-leaflet-loader.js (1)

53-165: Loader snippet structure looks solid overall.

The CDN-loading IIFE, SRI integrity attributes from local hashes, plugin synchronization pattern, error banners, and graceful degradation are well thought out. The waitForLeaflet polling is defensive but reasonable for robustness.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread webpack-plugins/inject-leaflet-loader.js Outdated
Comment thread webpack-plugins/inject-leaflet-loader.js Outdated
@nemesifier
Copy link
Copy Markdown
Member

nemesifier commented Feb 6, 2026

@coderabbitai Your suggestion:

206-211: The do…while loop is redundant with a g-flag regex.

Triggers CodeQL:

Incomplete multi-character sanitization

This string may still contain [<script](1), which may cause an HTML element injection vulnerability.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 6, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find files that might contain the code at lines 206-211
# Focus on webpack-plugins/inject-leaflet-loader.js based on context
cat webpack-plugins/inject-leaflet-loader.js | head -220 | tail -20

Length of output: 880


🏁 Script executed:

#!/bin/bash
# Get more context around lines 206-211 to understand the sanitization logic
sed -n '200,220p' webpack-plugins/inject-leaflet-loader.js

Length of output: 922


🏁 Script executed:

#!/bin/bash
# Search for do...while patterns in the file to locate the specific code
rg -n "do\s*\{" webpack-plugins/inject-leaflet-loader.js -A 5 -B 2

Length of output: 405


@nemesifier Thank you for catching this! You're absolutely right—my suggestion was incorrect from a security perspective.

Why CodeQL flagged it:

Regex-based HTML sanitization is inherently vulnerable to nested or overlapping tags. For example:

  • Input: <scr<script>ipt>malicious</script>
  • After one replacement: <script>malicious</script> (still vulnerable!)

The do...while loop was attempting to handle this by replacing iteratively, but this pattern is still incomplete and can be bypassed with clever nesting.

Recommended approach:

  1. Keep the do...while loop if you want to maintain the current approach (it's at least safer than a single replace)
  2. Better solution: Use a proper HTML parser instead of regex for HTML manipulation. For example:
    const { JSDOM } = require('jsdom');
    const dom = new JSDOM(html);
    // Use DOM methods to safely remove scripts
    dom.window.document.querySelectorAll('script[src*="leaflet.js"]').forEach(el => el.remove());
    html = dom.serialize();

That said, in this specific context (a webpack plugin processing HTML it generated during build), the risk is somewhat mitigated since you control the input. However, CodeQL is correctly warning about a pattern that's generally unsafe.

I apologize for the flawed suggestion. Security should always take precedence over minor code simplifications.

🐰💭
(Taking notes on HTML sanitization patterns...)


🧠 Learnings used
Learnt from: codesankalp
Repo: openwisp/netjsongraph.js PR: 425
File: src/js/netjsongraph.render.js:1-26
Timestamp: 2026-01-20T16:51:06.213Z
Learning: In the netjsongraph.js project, imports from `echarts/lib/chart/*/install`, `echarts/lib/component/*/install`, and `echarts/lib/renderer/install*` paths produce better tree-shaking results than the public entry points (`echarts/charts`, `echarts/components`, `echarts/renderers`) in their webpack configuration. The project maintainer has tested both approaches and confirmed the `/lib/*/install` pattern yields smaller bundles.

Copy link
Copy Markdown
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

@codesankalp good for me! I did some changes to make @coderabbitai happy, I think is good enough now, can you double check the changes do not break anything? I checked on my end but it's better if you double check before we merge.

@nemesifier
Copy link
Copy Markdown
Member

@coderabbitai is there any other issue which was not addressed yet?

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 6, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for sanitization-related code with do-while patterns or script tag removal
rg -n -C5 "do\s*\{" --type=js

# Also search for script tag removal patterns
rg -n -C5 "<script" --type=js

# Find files around 206-211 lines that might contain sanitization
fd -e js -x sh -c 'lines=$(wc -l < "{}"); if [ "$lines" -ge 200 ] && [ "$lines" -le 250 ]; then echo "{}"; fi'

Length of output: 278234


@nemesifier Thanks for flagging this — you’re right. Dropping the do…while in favor of a single g-flag replace isn’t just a functional regression; more importantly, regex-based “sanitization” of HTML is what CodeQL is warning about (multi-character evasion like <scr<script>ipt>). The safer fix is to stop sanitizing and instead parse the HTML and remove the offending nodes structurally.

Proposal:

  • Replace the regex/do…while block in webpack-plugins/inject-leaflet-loader.js with DOM parsing and selective removal of tags whose src/href match Leaflet (and plugin) assets. This avoids the sanitization pattern entirely and should satisfy CodeQL.
  • Minimal impact: keep the existing HtmlWebpackPlugin beforeEmit path; add a tiny parser dependency (node-html-parser or cheerio).

Illustrative change (using node-html-parser, but cheerio works too):

  1. Add a lightweight parser
  • package.json devDependencies:
    "node-html-parser": "^6.1.11"
  1. In webpack-plugins/inject-leaflet-loader.js, near the top:
    const { parse } = require('node-html-parser');

  2. Replace the regex removal block (Lines 202–211 and plugin CSS/JS removal right after) with:

// Remove hardcoded Leaflet assets via structured DOM removal (no regex sanitization)
const root = parse(html, { script: true, style: true, lowerCaseTagName: true });

// Remove leaflet.css
root.querySelectorAll('link[rel="stylesheet"]').forEach(el => {
const href = el.getAttribute('href') || '';
if (/leaflet(.min)?.css(?.*)?$/i.test(href)) el.remove();
});

// Remove main leaflet.js
root.querySelectorAll('script').forEach(el => {
const src = el.getAttribute('src') || '';
if (/leaflet(.min)?.js(?.*)?$/i.test(src)) el.remove();
});

// For plugin page, remove Leaflet plugin assets we load dynamically
if (hasPlugins) {
root.querySelectorAll('script').forEach(el => {
const src = el.getAttribute('src') || '';
if (/(leaflet-(draw|measure)(.min)?.js)(?.)?$/i.test(src)) el.remove();
});
root.querySelectorAll('link[rel="stylesheet"]').forEach(el => {
const href = el.getAttribute('href') || '';
if (/(leaflet-(draw|measure)(.min)?.css)(?.
)?$/i.test(href)) el.remove();
});
}

html = root.toString();

Why this helps

  • We’re no longer “sanitizing” strings; we’re removing exact nodes by attribute, which addresses CodeQL’s incomplete multi-character sanitization concern and is robust to spacing/attribute ordering.
  • Functionally identical to the intent of the previous code (removing hardcoded Leaflet assets so the loader can inject them), without relying on brittle regex.

If you prefer zero new deps, we can alternatively switch to HtmlWebpackPlugin’s tag hooks (alterAssetTags/alterAssetTagGroups) and filter out the script/link tag objects before templating, which also avoids regex and satisfies CodeQL. I can draft that version as well.

Would you like me to proceed with the parser-based change (and add the devDependency), or should I wire this into HtmlWebpackPlugin’s tag hooks instead?


🧠 Learnings used
Learnt from: codesankalp
Repo: openwisp/netjsongraph.js PR: 425
File: src/js/netjsongraph.render.js:1-26
Timestamp: 2026-01-20T16:51:06.213Z
Learning: In the netjsongraph.js project, imports from `echarts/lib/chart/*/install`, `echarts/lib/component/*/install`, and `echarts/lib/renderer/install*` paths produce better tree-shaking results than the public entry points (`echarts/charts`, `echarts/components`, `echarts/renderers`) in their webpack configuration. The project maintainer has tested both approaches and confirmed the `/lib/*/install` pattern yields smaller bundles.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 6, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for the HTML sanitization code that was flagged by CodeQL
cat webpack-plugins/inject-leaflet-loader.js | sed -n '200,220p'

Length of output: 922


🏁 Script executed:

#!/bin/bash
# Use GitHub CLI to check for unresolved review comments on PR `#425`
gh pr view 425 --repo openwisp/netjsongraph.js --json comments,reviews | jq '.reviews[] | select(.state == "CHANGES_REQUESTED" or .state == "COMMENTED")'

Length of output: 221905


🏁 Script executed:

#!/bin/bash
# Check CI status for the PR
gh pr checks 425 --repo openwisp/netjsongraph.js

Length of output: 1049


🏁 Script executed:

#!/bin/bash
# Get the full PR status
gh pr view 425 --repo openwisp/netjsongraph.js --json title,state,isDraft,reviewDecision,statusCheckRollup

Length of output: 2852


@nemesifier, based on my review of the PR state and recent activity, here's the current status:

Resolved Issues

  • All CI tests passing: dev, prod, and echarts-only builds all successful
  • Leaflet plugins example: Fixed (browser tests now passing)
  • Code coverage: Increased by 0.4% to 81.1%
  • Build strategy: Matrix testing for all variants working correctly

⚠️ Remaining Technical Concerns

From the most recent CodeRabbit review, there appear to be a few minor issues still flagged:

  1. HTML sanitization in webpack-plugins/inject-leaflet-loader.js (lines ~206-211):

    • The do...while loop for removing script tags was flagged by CodeQL as potentially incomplete for security
    • CodeRabbit noted this pattern can be bypassed with nested tags like <scr<script>ipt>
    • However, since this is build-time HTML processing of webpack-generated output (not user input), the risk is mitigated
  2. Minor code quality issues in the webpack plugin:

    • Variable shadowing (constructor parameter)
    • Some regex patterns that could be simplified

🎯 Recommendation

Since all functional tests are passing and the security concern is mitigated by the controlled build-time context, these remaining items are minor code quality improvements rather than blocking issues. You could:

  • Option A: Merge as-is since all tests pass and the code works correctly
  • Option B: Address the minor linting/code quality items for cleaner code

The PR has achieved its primary objectives: significant size reduction, dual build variants, and all functionality working correctly across all test scenarios.


🧠 Learnings used
Learnt from: codesankalp
Repo: openwisp/netjsongraph.js PR: 425
File: src/js/netjsongraph.render.js:1-26
Timestamp: 2026-01-20T16:51:06.213Z
Learning: In the netjsongraph.js project, imports from `echarts/lib/chart/*/install`, `echarts/lib/component/*/install`, and `echarts/lib/renderer/install*` paths produce better tree-shaking results than the public entry points (`echarts/charts`, `echarts/components`, `echarts/renderers`) in their webpack configuration. The project maintainer has tested both approaches and confirmed the `/lib/*/install` pattern yields smaller bundles.

@nemesifier
Copy link
Copy Markdown
Member

@coderabbitai let's not waste more time on little details that are not going to have any real impact on the project and let's focus on critical matters that bring real benefits to our users, as long as there's no real issues being introduced, minor things which require significant more effort for no noticeable benefits can be skipped.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 6, 2026

Tip

For best results, initiate chat on the files or code changes.

@nemesifier Absolutely understood! You're right—the PR has delivered substantial value (significant size reduction, dual build variants, all tests passing) and the remaining items are indeed minor polish that don't justify the effort.

Status: No critical issues blocking this PR.

The HTML sanitization pattern and minor linting items have no real-world impact in this build-time context. The PR is ready to merge from a functional and quality perspective.

I'll focus future reviews on issues that materially impact users, performance, security, or maintainability—not perfectionist code style tweaks. Thank you for the clear guidance!


✏️ Learnings added
Learnt from: nemesifier
URL: https://github.com/openwisp/netjsongraph.js/pull/425

Timestamp: 2026-02-06T16:33:48.448Z
Learning: In the openwisp/netjsongraph.js project, prioritize pragmatic reviews that focus on critical matters with real user impact (functionality, performance, security, maintainability) over minor code quality details that require significant effort for no noticeable benefit. Avoid perfectionist suggestions when tests pass and functionality is correct.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: codesankalp
Repo: openwisp/netjsongraph.js PR: 425
File: src/js/netjsongraph.render.js:1-26
Timestamp: 2026-01-20T16:51:06.213Z
Learning: In the netjsongraph.js project, imports from `echarts/lib/chart/*/install`, `echarts/lib/component/*/install`, and `echarts/lib/renderer/install*` paths produce better tree-shaking results than the public entry points (`echarts/charts`, `echarts/components`, `echarts/renderers`) in their webpack configuration. The project maintainer has tested both approaches and confirmed the `/lib/*/install` pattern yields smaller bundles.

@nemesifier nemesifier merged commit 82f6d59 into master Feb 6, 2026
10 checks passed
@nemesifier nemesifier deleted the fix/issue-392 branch February 6, 2026 18:00
@github-project-automation github-project-automation Bot moved this from In progress to Done in 26.05 Release Feb 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file enhancement javascript Pull requests that update Javascript code

Development

Successfully merging this pull request may close these issues.

[change] Reduce library size

4 participants