-
-
Notifications
You must be signed in to change notification settings - Fork 109
[change] Reduce library size #392 #425
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
7b1d3ca to
5236b0e
Compare
nemesifier
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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:
The contenthash in filenames will optimize CDN cache management when using cdn (if we are using cdn). Integration Considerations:
let me know your thoughts! |
nemesifier
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@codesankalp what do you think about the following?
- a result set providing the current output: all separate files
- a result set providing the library + echarts
- a result set providing the library + echarts + leaflet
Can you add basic explanation about the above?
68ed674 to
17ffba3
Compare
|
New Buid Sizes:
|
e43988e to
5355c03
Compare
0c99ec8 to
b4bbfa9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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?
b4bbfa9 to
4d026ce
Compare
nemesifier
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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?
bc058f2 to
e6c4b28
Compare
Signed-off-by: Sankalp <sankalp.nex@gmail.com>
c09691f to
e0fc528
Compare
📝 WalkthroughWalkthroughThis PR splits builds into Full and ECharts-only variants, adds a runtime Leaflet loader module, refactors ECharts usage to modular imports from echarts/core, moves Leaflet to an optional peer dependency, makes NetJSONGraph.render asynchronous, removes several example pages and a large data asset, updates package.json scripts/deps, introduces a webpack multi-build config with compression and bundle analysis, and adds an InjectLeafletLoaderPlugin to inject runtime Leaflet loading for echarts-only HTML outputs. Sequence Diagram(s)sequenceDiagram
participant Dev as Developer
participant Webpack as Webpack Build
participant Plugin as InjectLeafletLoaderPlugin
participant Output as HTML Output
participant Browser as Browser Runtime
participant Loader as getLeaflet
participant ECharts as ECharts (core)
participant Leaflet as Leaflet
Note over Dev,Webpack: Build Time (echarts-only)
Dev->>Webpack: yarn build:echarts-only
Webpack->>Webpack: set BUILD_TYPE=echarts-only
Webpack->>Plugin: apply plugin (process HTML)
Plugin->>Output: remove hardcoded Leaflet assets
Plugin->>Output: inject loader snippet wrapping example scripts
Webpack->>Output: emit modified HTML
Note over Browser,Loader: Runtime
Browser->>Output: load page
Output->>Loader: execute injected loader snippet
Loader->>Leaflet: attempt load (window.L or CDN)
alt Leaflet available
Leaflet->>Browser: provide API
else Leaflet missing
Loader-->>Browser: return undefined (suppress error optional)
end
Browser->>ECharts: call registerLeafletSystem() (runtime)
ECharts->>Loader: getLeaflet() for coord system / CRS
Browser->>Output: execute original example script which may initialize graphs/maps
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/js/netjsongraph.core.js (1)
80-142: Async method doesn't return or await its promise chain.The
render()method is markedasyncbut doesn'treturnorawaitthe promise frompaginatedDataParse. This means callingawait render()will resolve immediately without waiting for data processing to complete, making theasynckeyword misleading.Proposed fix - return the promise chain
async render() { const [JSONParam, ...resParam] = this.JSONParam; this.config.onRender.call(this); this.event.once("onReady", this.config.onReady.bind(this)); this.event.once("onLoad", this.config.onLoad.bind(this)); - this.utils.paginatedDataParse + await this.utils.paginatedDataParse .call(this, JSONParam) .then((JSONData) => { // ... existing logic }) .catch((error) => { console.error(error); });Or alternatively, convert the
.then()chain totry/catchwithawaitfor consistency with the async pattern.
🤖 Fix all issues with AI agents
In `@package.json`:
- Around line 87-97: Remove "leaflet" from the "dependencies" section in
package.json so it is only declared as a peer dependency (and optional via
"peerDependenciesMeta"); update the package.json to leave "leaflet" only under
"peerDependencies" and "peerDependenciesMeta" (keeping "zrender" and other
actual runtime deps in "dependencies") so the echarts-only build will not bundle
Leaflet and consumers must provide it.
In `@src/js/netjsongraph.render.js`:
- Around line 213-214: Remove the now-dead conditional branches that test
params.componentSubType === "graphGL" in the event-handling code (the two
occurrences flagged in netjsongraph.render.js); delete those if/else branches
and any variables or fallback logic that only applied to "graphGL", and ensure
events fall through to the existing "graph" handling path (keep existing
handlers that use the generic graph type). Locate the checks by searching for
params.componentSubType === "graphGL" and remove the entire branch bodies so
only the active graph event logic remains; run the component event flows to
confirm no behavior regressions.
🧹 Nitpick comments (7)
src/js/echarts-leaflet/LeafletView.js (1)
38-39: Consider removing the unused parameter instead of suppressing the lint warning.The
eparameter inmoveHandleris never used. Since the function is called directly inzoomHandler()(line 86) without arguments, you could remove it entirely rather than suppressing the warning.Proposed fix
- // eslint-disable-next-line no-unused-vars - function moveHandler(e) { + function moveHandler() {src/js/echarts-leaflet/LeafletCoordSys.js (1)
76-81: Consider simplifying the nested ternary for readability.The chained ternary for
coordSysresolution works but is somewhat dense. This is acceptable given the/* eslint-disable */block, but a future refactor could extract this logic.♻️ Optional: Clearer coordSys resolution
- const coordSys = leafletModel - ? leafletModel.coordinateSystem - : seriesModel - ? seriesModel.coordinateSystem || // For map. - (seriesModel.getReferringComponents("leaflet")[0] || {}).coordinateSystem - : null; + let coordSys = null; + if (leafletModel) { + coordSys = leafletModel.coordinateSystem; + } else if (seriesModel) { + coordSys = seriesModel.coordinateSystem || + (seriesModel.getReferringComponents("leaflet")[0] || {}).coordinateSystem; + }webpack-plugins/inject-leaflet-loader.js (2)
102-104: Rename variable to avoid shadowing globalconstructor.The static analysis tool correctly identifies that
constructorshadows the global property. Renaming improves clarity.♻️ Rename shadowed variable
const HtmlWebpackPlugin = compiler.options.plugins - .map((plugin) => plugin.constructor) - .find((constructor) => constructor && constructor.name === "HtmlWebpackPlugin"); + .map((plugin) => plugin.constructor) + .find((ctor) => ctor && ctor.name === "HtmlWebpackPlugin");
140-145: Regex-based script removal may be fragile.The do-while loop removes Leaflet script tags iteratively. This approach works but could be simplified. Consider if a single global replace would suffice.
♻️ Simpler approach using global replace
- const leafletJSRegex = /<script[^>]*leaflet[^>]*>[\s\S]*?<\/script>/gi; - let prev; - do { - prev = html; - html = html.replace(leafletJSRegex, ""); - } while (html !== prev); + // Remove all leaflet script tags in one pass + html = html.replace(/<script[^>]*leaflet[^>]*>[\s\S]*?<\/script>/gi, "");Note: The loop may have been intentional for nested matches, but
giflag handles multiple occurrences.webpack.config.js (3)
22-56:unsafe*Terser flags can change runtime semantics.Line 49-55 enables multiple
unsafe*options. These can introduce subtle runtime changes (e.g., around-0, getters, or prototype checks). If you keep them for size, please gate behind an explicit env flag and validate the minified build with production tests.💡 Possible gating to make unsafe options opt-in
const getMinimizers = (isProduction) => { const minimizers = []; if (isProduction) { + const enableUnsafe = process.env.TERSER_UNSAFE === "true"; minimizers.push( new TerserPlugin({ extractComments: false, parallel: true, terserOptions: { compress: { passes: 3, dead_code: true, unused: true, toplevel: true, reduce_vars: true, collapse_vars: true, evaluate: true, conditionals: true, sequences: true, properties: true, booleans: true, if_return: true, join_vars: true, side_effects: true, negate_iife: true, hoist_funs: true, hoist_vars: false, pure_getters: true, - unsafe: true, - unsafe_comps: true, - unsafe_Function: true, - unsafe_math: true, - unsafe_proto: true, - unsafe_regexp: true, - unsafe_undefined: true, + ...(enableUnsafe + ? { + unsafe: true, + unsafe_comps: true, + unsafe_Function: true, + unsafe_math: true, + unsafe_proto: true, + unsafe_regexp: true, + unsafe_undefined: true, + } + : {}), },
179-209: Ensure hosting serves the generated.gz/.brassets.The compression plugins will emit precompressed files, but they won’t help unless your static server is configured to serve them with the proper
Content-Encodingheaders.
77-88: Consider usingpublicPath: "auto"for better deployment flexibility.This library bundle currently uses absolute
publicPath: "/", which works for root-level hosting but assumes the library will always be served from the site root. For a reusable library that may be hosted at different paths or from CDNs with custom base URLs,publicPath: "auto"(supported in webpack 5.84.0+) is a more resilient choice that adapts to the runtime context.Suggested change
- publicPath: "/", + publicPath: "auto",
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
lib/js/echarts-gl.min.jsis excluded by!**/*.min.jsyarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (25)
.prettierrcREADME.mdindex.htmlpackage.jsonpublic/assets/data/airplaneRouteMap.jsonpublic/assets/data/netjsongraph-graphGL.jsonpublic/example_templates/netjsongraph-graphGL.htmlpublic/example_templates/netjsonmap-animation.htmlpublic/example_templates/netjsonmap-indoormap.htmlsrc/js/echarts-leaflet/LeafletCoordSys.jssrc/js/echarts-leaflet/LeafletModel.jssrc/js/echarts-leaflet/LeafletView.jssrc/js/echarts-leaflet/index.jssrc/js/leaflet-loader.jssrc/js/netjsongraph.clients.jssrc/js/netjsongraph.config.jssrc/js/netjsongraph.core.jssrc/js/netjsongraph.geojson.jssrc/js/netjsongraph.jssrc/js/netjsongraph.render.jstest/browser.test.utils.jstest/netjsongraph.duplicateNodes.test.jstest/netjsongraph.render.test.jswebpack-plugins/inject-leaflet-loader.jswebpack.config.js
💤 Files with no reviewable changes (4)
- public/example_templates/netjsongraph-graphGL.html
- public/example_templates/netjsonmap-animation.html
- test/browser.test.utils.js
- public/assets/data/airplaneRouteMap.json
🧰 Additional context used
🧠 Learnings (3)
📚 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.core.jssrc/js/netjsongraph.clients.jstest/netjsongraph.render.test.jssrc/js/netjsongraph.render.jstest/netjsongraph.duplicateNodes.test.jssrc/js/netjsongraph.geojson.jssrc/js/netjsongraph.config.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.jstest/netjsongraph.duplicateNodes.test.js
📚 Learning: 2026-01-20T00:42:26.416Z
Learnt from: dee077
Repo: openwisp/netjsongraph.js PR: 417
File: src/js/netjsongraph.core.js:132-145
Timestamp: 2026-01-20T00:42:26.416Z
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 (11)
webpack-plugins/inject-leaflet-loader.js (1)
webpack.config.js (2)
InjectLeafletLoaderPlugin(10-10)HtmlWebpackPlugin(5-5)
test/netjsongraph.render.test.js (2)
src/js/netjsongraph.geojson.js (1)
map(142-142)test/netjsongraph.duplicateNodes.test.js (1)
NetJSONGraphRender(2-2)
src/js/leaflet-loader.js (4)
src/js/echarts-leaflet/LeafletCoordSys.js (1)
L(12-12)src/js/echarts-leaflet/index.js (1)
L(11-11)src/js/netjsongraph.config.js (1)
L(364-364)src/js/netjsongraph.geojson.js (2)
L(136-136)L(140-140)
src/js/netjsongraph.render.js (3)
src/js/echarts-leaflet/index.js (1)
L(11-11)src/js/leaflet-loader.js (2)
L(12-12)getLeaflet(14-38)src/js/netjsongraph.geojson.js (2)
L(136-136)L(140-140)
src/js/netjsongraph.geojson.js (4)
src/js/echarts-leaflet/LeafletCoordSys.js (1)
L(12-12)src/js/echarts-leaflet/index.js (1)
L(11-11)src/js/leaflet-loader.js (2)
L(12-12)getLeaflet(14-38)src/js/netjsongraph.config.js (1)
L(364-364)
webpack.config.js (2)
test/netjsongraph.duplicateNodes.test.js (1)
require(1-1)test/netjsonWorker.test.js (1)
require(1-1)
src/js/echarts-leaflet/index.js (3)
src/js/echarts-leaflet/LeafletCoordSys.js (1)
L(12-12)src/js/leaflet-loader.js (2)
L(12-12)getLeaflet(14-38)src/js/echarts-leaflet/LeafletModel.js (1)
extendLeafletModel(6-50)
src/js/echarts-leaflet/LeafletCoordSys.js (3)
src/js/echarts-leaflet/index.js (1)
L(11-11)src/js/leaflet-loader.js (2)
L(12-12)getLeaflet(14-38)src/js/netjsongraph.geojson.js (3)
L(136-136)L(140-140)map(142-142)
src/js/netjsongraph.config.js (4)
src/js/echarts-leaflet/LeafletCoordSys.js (1)
L(12-12)src/js/echarts-leaflet/index.js (1)
L(11-11)src/js/leaflet-loader.js (2)
L(12-12)getLeaflet(14-38)src/js/netjsongraph.geojson.js (2)
L(136-136)L(140-140)
src/js/netjsongraph.js (1)
src/js/echarts-leaflet/index.js (1)
registerLeafletSystem(10-37)
src/js/echarts-leaflet/LeafletView.js (1)
src/js/echarts-leaflet/LeafletCoordSys.js (3)
mapOffset(126-126)mapOffset(132-132)api(113-113)
🪛 Biome (2.1.2)
webpack-plugins/inject-leaflet-loader.js
[error] 104-104: 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 (31)
.prettierrc (1)
4-5: LGTM!The
printWidth: 88setting aligns well with common formatting standards (e.g., Python's black formatter) and provides consistency across the project.README.md (1)
138-173: LGTM!The new "Building the library" section clearly documents both build variants with appropriate use cases, outputs, and requirements. The documentation aligns well with the PR objectives for size reduction and provides clear guidance for integrators.
public/example_templates/netjsonmap-indoormap.html (1)
13-17: LGTM!Adding the Leaflet JS script from the CDN is necessary to support the echarts-only build variant. The SRI integrity hash and crossorigin attribute provide proper security for the external resource. This correctly enables the
Lnamespace usage throughout the example.src/js/netjsongraph.clients.js (3)
1-2: LGTM!Importing
graphicdirectly fromecharts/corealigns with the tree-shakeable modular ECharts pattern and removes dependency on globalechartsreferences. This is consistent with the PR's broader refactoring approach.
61-61: Correct usage of modular graphic API.The
graphic.Groupinstantiation properly uses the imported module instead of the previous global reference.
136-144: Correct usage of modular graphic API.The
graphic.Circleinstantiation properly uses the imported module for creating client dot overlays.src/js/echarts-leaflet/LeafletModel.js (3)
1-2: LGTM!Importing
extendComponentModeldirectly fromecharts/coreis the correct modular approach for ECharts 5.x component registration.
6-6: Clean API simplification.Removing the
echartsparameter from the function signature simplifies the API since the required function is now imported directly. This aligns with the broader refactoring to use modular ECharts imports.
17-49: LGTM!The component model registration correctly uses the imported
extendComponentModelfunction. The model definition with its methods (getLeaflet,setCenterAndZoom,centerOrZoomChanged) anddefaultOptionremains functionally unchanged.index.html (3)
36-42: LGTM!The CSS transition property reformatted to multi-line improves readability, and the quote standardization to double quotes provides consistency.
145-239: Removal of unused example cards aligns with PR objectives.The removal of
netjsongraph-graphGL.htmlandnetjsonmap-animation.htmldemo cards aligns with issue#392's objective to reduce bundle size by removing unused features and examples.
241-263: LGTM!The theme toggle script logic is correct. The quote standardization to double quotes is consistent with the formatting changes throughout the file.
package.json (1)
6-13: LGTM!The
sideEffects: falsedeclaration enables tree-shaking optimization, and the new build scripts properly support the dual build variants (full and echarts-only) via theBUILD_TYPEenvironment variable.test/netjsongraph.duplicateNodes.test.js (1)
2-2: LGTM!The import pattern correctly retrieves the default export from the refactored module, aligning with the broader change where
NetJSONGraphRenderis now exported as a default export rather than a named export.src/js/netjsongraph.geojson.js (1)
136-140: LGTM!The dynamic Leaflet loading with proper null guard ensures graceful handling when Leaflet is unavailable (echarts-only build). This pattern is consistent with usage in other files like
LeafletCoordSys.jsandindex.js.src/js/echarts-leaflet/LeafletView.js (1)
1-7: LGTM!The refactor from echarts globals to modular imports (
extendComponentView,getInstanceByDom) aligns with echarts 5.x best practices and enables better tree-shaking. The parameterless function signature simplifies the API since dependencies are now imported directly.src/js/leaflet-loader.js (1)
14-37: LGTM!The loader correctly implements a caching strategy with conditional loading based on the build type. The
suppressErrorparameter is appropriately used by callers likesrc/js/echarts-leaflet/index.jsto gracefully handle optional Leaflet scenarios. Thetypeof windowcheck ensures compatibility with SSR/Node environments.test/netjsongraph.render.test.js (2)
1-3: LGTM! Import changes align with new module structure.The updated imports correctly reflect the shift to default exports for
NetJSONGraphRenderand explicit Leaflet import, matching the broader refactoring in this PR.
705-713: Good test isolation practice.Preserving and restoring the original prototype in
beforeEach/afterEachensures clustering tests don't leak prototype modifications to other tests.src/js/echarts-leaflet/LeafletCoordSys.js (1)
11-24: Clean factory pattern with runtime Leaflet loading.The early validation and destructuring of Leaflet utilities is well-structured. This ensures the coordinate system is only created when Leaflet is actually available.
src/js/netjsongraph.config.js (1)
360-369: Well-designed dynamic CRS getter.The getter pattern correctly defers Leaflet access until runtime, returning
nullwhen Leaflet is unavailable. This enables echarts-only builds to function without Leaflet while preserving full functionality when Leaflet is present.src/js/netjsongraph.render.js (2)
1-26: Excellent modular ECharts integration.The shift to explicit chart/component/renderer imports from
echarts/coreenables tree-shaking and significantly reduces bundle size. Theuse()call registers only what's needed.
464-468: Proper Leaflet validation for map rendering.Throwing early when Leaflet is unavailable provides a clear error message rather than cryptic runtime failures. The destructuring of
circleMarkerandlatLngBoundsis clean.src/js/netjsongraph.js (3)
9-9: Good one-time registration pattern.The
isLeafletRegisteredguard ensures the Leaflet coordinate system is registered exactly once, preventing duplicate registration errors when multiple NetJSONGraph instances are created.Also applies to: 73-76
77-79: Directinitusage improves tree-shaking.Using
initfromecharts/coreinstead ofecharts.initallows bundlers to eliminate unused ECharts code, contributing to the size reduction goal.
172-172: No changes required—this concern does not apply.The library does not and has not exposed
window.echartsorwindow.Las global objects. Onlywindow.NetJSONGraphis exposed at line 172. The library stores ECharts and Leaflet as instance properties (this.echarts,this.leaflet), while external code is expected to provide Leaflet separately as a globalLobject when needed (as documented in README.md line 172). There is no breaking change to integrations.Likely an incorrect or invalid review comment.
webpack-plugins/inject-leaflet-loader.js (1)
35-36: Leaflet version is already consistent.The CDN URLs correctly use Leaflet 1.8.0, which matches the minimum version specified in
package.json(^1.8.0). The pinned CDN version prevents unintended updates while allowing flexibility in local dependencies. No changes needed.webpack.config.js (3)
111-124: CSS + asset/resource rules look solid.The CSS loader and asset/resource generator setup is straightforward and aligns with the new bundling goals.
3-10: I need the review comment to rewrite. Please provide the review comment within<review_comment>tags so I can proceed with verification and rewriting.
96-101: Review comment is incorrect regarding webpack optimization behavior.The claim that
optimization.sideEffects: false"can drop CSS/side-effect imports" mischaracterizes webpack's behavior. Settingoptimization.sideEffects: falsedisables side-effects-based tree-shaking, meaning webpack will NOT remove modules based on side-effects analysis. This actually reduces the risk of CSS being dropped.The actual concern lies with
"sideEffects": falseinpackage.json(line 6), which marks the entire library as side-effect-free. However, since CSS is loaded through style-loader/css-loader (not direct CSS imports in source), and the webpack config disables side-effects pruning, CSS is protected from tree-shaking removal in the current setup.Changing the webpack config setting to
truewould enable side-effects analysis, which combined with the package.json declaration, could actually create the problem described—not fix it.Likely an incorrect or invalid review comment.
src/js/echarts-leaflet/index.js (1)
10-22: The review comment is based on an incorrect understanding of the code location. Theconst L = getLeaflet();inLeafletCoordSys.jsis inside thecreateLeafletCoordSystem()function (line 12), not at module scope (line 11 is the function declaration). SincegetLeaflet()is called at runtime whencreateLeafletCoordSystem()is invoked, the code already supports dynamic Leaflet loading without module-scope caching. No changes are needed.Likely an incorrect or invalid review comment.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
Signed-off-by: Sankalp <sankalp.nex@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/js/netjsongraph.render.js`:
- Around line 1-26: The imports use legacy private paths (echarts/lib/...) which
hurt tree-shaking; replace them with the public entry points and named exports:
import GraphChart, LinesChart, ScatterChart from "echarts/charts"; import
TooltipComponent, TitleComponent, ToolboxComponent, LegendComponent,
GraphicComponent from "echarts/components"; import SVGRenderer, CanvasRenderer
from "echarts/renderers"; keep the existing use([GraphChart, LinesChart,
TooltipComponent, TitleComponent, ToolboxComponent, LegendComponent,
SVGRenderer, CanvasRenderer, ScatterChart, GraphicComponent]) call but remove
the "install as" style and the "echarts/lib/..." paths so the same symbols
(GraphChart, LinesChart, ScatterChart, TooltipComponent, TitleComponent,
ToolboxComponent, LegendComponent, GraphicComponent, SVGRenderer,
CanvasRenderer) are imported from the public modules for proper tree-shaking.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
package.jsonsrc/js/netjsongraph.render.js
🚧 Files skipped from review as they are similar to previous changes (1)
- package.json
🧰 Additional context used
🧠 Learnings (2)
📚 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:
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.render.js
🧬 Code graph analysis (1)
src/js/netjsongraph.render.js (5)
src/js/echarts-leaflet/index.js (1)
L(11-11)src/js/leaflet-loader.js (2)
L(12-12)getLeaflet(14-38)src/js/netjsongraph.config.js (1)
L(364-364)src/js/echarts-leaflet/LeafletCoordSys.js (1)
L(12-12)src/js/netjsongraph.geojson.js (2)
L(136-136)L(140-140)
🔇 Additional comments (7)
src/js/netjsongraph.render.js (7)
203-212: LGTM! Simplified graph series configuration.Removing the conditional graphGL handling in favor of a consistent
type: "graph"with a default"force"layout is a good simplification. This eliminates the echarts-gl dependency while maintaining standard force-directed layout functionality.
325-331: LGTM! Simplified to standard scatter series.Switching from conditional
effectScatterto a plainscatterseries type reduces complexity and bundle size by eliminating the animation effects component. This is a reasonable trade-off for the size reduction objectives.
458-462: LGTM! Proper Leaflet availability guard.The guard correctly checks for Leaflet availability via
getLeaflet()and throws a descriptive error early if unavailable. DestructuringcircleMarkerandlatLngBoundsimmediately after the guard ensures they're available for the rest of the method.
490-504: LGTM! Consistent use of destructured Leaflet utilities.The
circleMarkerfunction is correctly used from the destructured Leaflet instance, maintaining consistency with the dynamic loading pattern.
524-534: LGTM! Proper bounds calculation with destructured latLngBounds.The
latLngBoundsfactory function is correctly used to create bounds from the node locations array. The[lat, lng]format is valid input for Leaflet's latLngBounds constructor.
675-685: LGTM! Simplified cluster click handling.The condition now correctly checks only for
"scatter"component subtype, aligned with the removal of the effectScatter path. The zoom-to-cluster behavior is preserved.
812-812: Default export is appropriate for the module's API.The change to a default export aligns with the module's actual public interface. The module only exports
NetJSONGraphRenderas intended for consumers;echartsandLare internal dependencies not exposed through this module's exports.Likely an incorrect or invalid review comment.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| import {use} from "echarts/core"; | ||
| import {install as LinesChart} from "echarts/lib/chart/lines/install"; | ||
| import {install as GraphChart} from "echarts/lib/chart/graph/install"; | ||
| import {install as ScatterChart} from "echarts/lib/chart/scatter/install"; | ||
| import {install as TooltipComponent} from "echarts/lib/component/tooltip/install"; | ||
| import {install as TitleComponent} from "echarts/lib/component/title/install"; | ||
| import {install as ToolboxComponent} from "echarts/lib/component/toolbox/install"; | ||
| import {install as LegendComponent} from "echarts/lib/component/legend/install"; | ||
| import {install as GraphicComponent} from "echarts/lib/component/graphic/install"; | ||
| import {install as SVGRenderer} from "echarts/lib/renderer/installSVGRenderer"; | ||
| import {install as CanvasRenderer} from "echarts/lib/renderer/installCanvasRenderer"; | ||
| import getLeaflet from "./leaflet-loader"; | ||
| import {addPolygonOverlays} from "./netjsongraph.geojson"; | ||
|
|
||
| echarts.use([ | ||
| use([ | ||
| GraphChart, | ||
| EffectScatterChart, | ||
| LinesChart, | ||
| TooltipComponent, | ||
| TitleComponent, | ||
| ToolboxComponent, | ||
| LegendComponent, | ||
| SVGRenderer, | ||
| CanvasRenderer, | ||
| ScatterChart, | ||
| GraphicComponent, | ||
| ]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
echarts 5 tree-shaking recommended import paths echarts/core vs echarts/lib
💡 Result:
Use the tree-shaking API: import from "echarts/core" plus specific subpackages (echarts/charts, echarts/components, echarts/renderers) and call echarts.use([...]). This gives the smallest bundles and is the recommended approach in ECharts 5. The legacy "echarts/lib/..." (or importing the whole "echarts" package) is supported for CommonJS/compat but does not tree-shake as well. [1][2]
References:
- ECharts handbook — Import / tree-shaking example. [1]
- v5 upgrade guide — introduces the new tree-shaking API and recommends it over echarts/lib. [2]
🌐 Web query:
echarts official documentation modular imports tree-shaking entry points
💡 Result:
Short summary — how to do modular imports, tree-shaking and which entry points to use in ECharts:
-
Tree-shakable API (recommended): import the core and then only the chart/component/feature/renderer modules you need, then register them with echarts.use([...]). You must import either CanvasRenderer or SVGRenderer. Example pattern:
import * as echarts from 'echarts/core';
import { BarChart } from 'echarts/charts';
import { TitleComponent, TooltipComponent } from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
echarts.use([BarChart, TitleComponent, TooltipComponent, CanvasRenderer]);. [1][2] -
Primary entry points:
- 'echarts' — legacy/all-in-one bundle (imports everything).
- 'echarts/core' — minimal core for tree-shaking (use with charts/components/features/renderers).
- 'echarts/charts' — chart series exports (e.g., BarChart, LineChart).
- 'echarts/components' — UI/coord/dataset components (e.g., TooltipComponent, GridComponent).
- 'echarts/features' — extra features (e.g., LabelLayout, UniversalTransition).
- 'echarts/renderers' — CanvasRenderer or SVGRenderer (one required). [1][2]
-
Notes:
- ECharts v5+ added this tree-shaking API (since v5.0.1); later versions use ESM by default (check docs for your installed version). [2][1]
- Use your bundler’s tree-shaking (ESM + production build) to actually eliminate unused modules.
Sources: ECharts handbook — Import ECharts (tree-shaking examples) and v5 upgrade notes. [1] https://echarts.apache.org/handbook/en/basics/import/ [2] https://echarts.apache.org/handbook/en/basics/release-note/v5-upgrade-guide/
Replace echarts/lib/* imports with the official public entry points for proper tree-shaking.
The code currently imports from echarts/lib/* paths, which are legacy and do not tree-shake as effectively as the recommended approach. According to the official echarts 5.x documentation, the tree-shaking API uses public entry points:
Instead of:
import {install as GraphChart} from "echarts/lib/chart/graph/install";Use:
import {GraphChart} from "echarts/charts";Similarly, replace echarts/lib/component/* with echarts/components and echarts/lib/renderer/* with echarts/renderers. The overall pattern remains correct (echarts/core + use()), but these specific import paths should be updated to the public entry points for optimal tree-shaking and bundle size reduction.
🤖 Prompt for AI Agents
In `@src/js/netjsongraph.render.js` around lines 1 - 26, The imports use legacy
private paths (echarts/lib/...) which hurt tree-shaking; replace them with the
public entry points and named exports: import GraphChart, LinesChart,
ScatterChart from "echarts/charts"; import TooltipComponent, TitleComponent,
ToolboxComponent, LegendComponent, GraphicComponent from "echarts/components";
import SVGRenderer, CanvasRenderer from "echarts/renderers"; keep the existing
use([GraphChart, LinesChart, TooltipComponent, TitleComponent, ToolboxComponent,
LegendComponent, SVGRenderer, CanvasRenderer, ScatterChart, GraphicComponent])
call but remove the "install as" style and the "echarts/lib/..." paths so the
same symbols (GraphChart, LinesChart, ScatterChart, TooltipComponent,
TitleComponent, ToolboxComponent, LegendComponent, GraphicComponent,
SVGRenderer, CanvasRenderer) are imported from the public modules for proper
tree-shaking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried with public entrypoint and tree shaking was not working. With individual import it is working.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
echarts tree-shaking echarts/lib install imports vs public entry points webpack bundle size
💡 Result:
Short answer: use the tree‑shakable public entry points (echarts/core + echarts/charts, components, features, renderers) and never import the whole package (import 'echarts' or require 'echarts/lib/echarts') if you care about bundle size. That gives the smallest bundles with modern bundlers; pulling the top-level entry or any non‑tree‑shakeable CommonJS path will include most of ECharts. [1][2]
Practical checklist
- Install: npm install echarts. [2]
- Preferred imports (tree‑shakable):
- import * as echarts from 'echarts/core';
- import { BarChart } from 'echarts/charts';
- import { TooltipComponent, GridComponent } from 'echarts/components';
- import { CanvasRenderer } from 'echarts/renderers';
- echarts.use([...]); — only modules you need. This minimizes bundle size. [1][2]
- Avoid:
- import 'echarts' or import * as echarts from 'echarts' — pulls everything. [2]
- require('echarts/lib/echarts') + require('echarts/lib/chart/...') is the old CommonJS approach and tends to be larger / less effective for modern tree shaking. Use only if you must support older toolchains. [1][8]
- Note about echarts/src vs echarts/lib: the ES-source under src can produce slightly smaller bundles, but the supported/public API is the tree‑shakable entry points; mixing src/lib is discouraged. [8]
- Webpack gotchas:
- Ensure your build targets ESM-aware toolchain (production mode, Terser, sideEffects:false where safe) so tree shaking actually removes unused exports. If you import any non‑tree‑shaken consumer (e.g., a component that imports the whole echarts package), tree shaking will fail and the bundle stays large. Community issues show tree‑shaking can appear ineffective if any module pulls full ECharts or if bundler config isn't ESM‑friendly. [6][7]
- Always import the renderer(s) you need (CanvasRenderer or SVGRenderer); missing renderer imports can cause accidental inclusion of both or fallback behavior. [1]
- If you still see a large bundle:
- Audit imports with webpack-bundle-analyzer to find which import pulls the big code.
- Check third‑party wrappers (echarts-for-react, ngx-echarts) — use their "core" variants or pass your own echarts instance to avoid them importing the whole package. [9][2]
- References: ECharts handbook (import & v5 tree‑shaking guide), custom‑build notes, and community reports on bundle-size issues. [1][2][8][6][9]
Sources
[1] ECharts v5 upgrade & tree‑shaking guide (handbook).
[2] ECharts "Import" handbook (tree‑shakable examples).
[6] GitHub issue: ngx‑echarts tree‑shaking / bundle size observations.
[7] Community blog showing effective tree‑shaking steps and results.
[8] ECharts custom build / src vs lib guidance.
[9] echarts‑for‑react docs (core import example).
@codesankalp, thank you for testing both approaches! Your empirical results are what matter most, especially for a bundle-size optimization PR. If the echarts/lib/*/install imports are producing smaller bundles in your specific webpack configuration, then that's the right choice to keep.
While the official docs generally recommend the public entry points (echarts/charts, echarts/components), tree-shaking effectiveness can vary based on bundler version, configuration, and the specific dependency graph. Since you've measured both approaches and confirmed the current pattern works better, please keep the echarts/lib/*/install imports.
Thanks for doing the due diligence on this!
✏️ Learnings added
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 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.
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.
0194a40 to
7590470
Compare
Should maintain consistent return type (undefined instead of null)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@webpack-plugins/inject-leaflet-loader.js`:
- Around line 139-144: The map callback currently uses a parameter named
"constructor", shadowing the global and triggering Biome lint; rename that
parameter (e.g., to "PluginConstructor" or "ctor") in the expression that builds
HtmlWebpackPlugin so the find call uses the new name (e.g., PluginConstructor &&
PluginConstructor.name === "HtmlWebpackPlugin") and update any references inside
the callback accordingly to eliminate the shadowing.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
lib/css/assets/leaflet-measure.pngis excluded by!**/*.png
📒 Files selected for processing (2)
src/js/leaflet-loader.jswebpack-plugins/inject-leaflet-loader.js
🚧 Files skipped from review as they are similar to previous changes (1)
- src/js/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-formatcommand with Python virtualenv enabled andyarn lint:fix(runs eslint --fix and prettier via lint-staged)
Files:
webpack-plugins/inject-leaflet-loader.js
🧠 Learnings (4)
📓 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
🧬 Code graph analysis (1)
webpack-plugins/inject-leaflet-loader.js (2)
webpack.config.js (2)
InjectLeafletLoaderPlugin(10-10)HtmlWebpackPlugin(5-5)test/browser.test.utils.js (1)
options(19-19)
🪛 Biome (2.1.2)
webpack-plugins/inject-leaflet-loader.js
[error] 142-142: 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)
⏰ 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
🔇 Additional comments (1)
webpack-plugins/inject-leaflet-loader.js (1)
201-225: Constructor shadowing in arrow function at line 142 requires rename.Line 142 uses
constructoras a parameter name, which shadows the built-inconstructorproperty and will likely trigger linting warnings. Rename it topluginConstructoror similar to avoid conflicts.Regarding lines 201–225 script wrapping: the regex pattern correctly handles the current HTML files (each has exactly one simple inline script with no extra attributes). However, the approach assumes a single inline script and doesn't tolerate extra attributes (e.g.,
defer,data-*) or multiline tag opening. If future examples deviate from this pattern—or if inline module scripts withimport/exportare added—the wrapping may fail or produce invalid syntax. The current implementation works for the existing templates but consider adding an explicit marker attribute (e.g.,data-njg-main) to the intended inline script for more robust selection if edge cases emerge.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| const HtmlWebpackPlugin = compiler.options.plugins | ||
| .map((plugin) => plugin.constructor) | ||
| .find( | ||
| (constructor) => | ||
| constructor && constructor.name === "HtmlWebpackPlugin", | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename shadowing variable to satisfy Biome lint.
Line 140–143 shadows the global constructor identifier and Biome flags it. Rename the callback parameter to avoid the lint error.
🔧 Suggested fix
- .map((plugin) => plugin.constructor)
- .find(
- (constructor) =>
- constructor && constructor.name === "HtmlWebpackPlugin",
- );
+ .map((plugin) => plugin.constructor)
+ .find(
+ (pluginCtor) =>
+ pluginCtor && pluginCtor.name === "HtmlWebpackPlugin",
+ );🧰 Tools
🪛 Biome (2.1.2)
[error] 142-142: 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)
🤖 Prompt for AI Agents
In `@webpack-plugins/inject-leaflet-loader.js` around lines 139 - 144, The map
callback currently uses a parameter named "constructor", shadowing the global
and triggering Biome lint; rename that parameter (e.g., to "PluginConstructor"
or "ctor") in the expression that builds HtmlWebpackPlugin so the find call uses
the new name (e.g., PluginConstructor && PluginConstructor.name ===
"HtmlWebpackPlugin") and update any references inside the callback accordingly
to eliminate the shadowing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reviewed and done some improvements, see the commit description for more info.
I really want to merge this but unfortunately some examples break:
- leaflet plugins
- multiple interfaces, this seems to fail also on master
- node expand or fold (we may remove this and the related functionality that implemetns this, see #492)
Other issues I see:
- The command to build the echarts only variant is not used in the CI at all, if it breaks in the future we won't notice, can we find a way to run also this command and verify if it's working fine?
| unsafe_math: true, | ||
| unsafe_proto: true, | ||
| unsafe_regexp: true, | ||
| unsafe_undefined: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure these unsafe options are really needed?
- `InjectLeafletLoaderPlugin` assumes specific HTML structure that may break - Uses regex to remove/replace script tags which is fragile
ba20b6c to
85097e8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@webpack-plugins/inject-leaflet-loader.js`:
- Around line 195-199: The inline-script regex is too restrictive (only allows
an optional type attribute) and misses scripts with other attrs (defer, nonce,
etc.), causing the loader not to be injected; update the matching logic around
the scriptRegex variable to match any <script ...> tag with arbitrary attributes
but no src attribute (e.g. capture attributes with
/<script\b([^>]*?)>\s*([\s\S]*?)\s*<\/script>/i and then ensure you only treat
tags without a src attribute as inline), preserve the full attributes string
when reinserting the modified script, and apply the same change to the other
occurrence around lines 216-219 so all inline scripts are detected and updated
correctly.
- Around line 204-214: The current logic detects module scripts via
scriptTypeMatch/scriptType but still wraps everything in an initMap function
when creating newScript, which breaks module semantics; modify the code around
scriptTypeMatch/scriptType and the newScript construction so that if scriptType
=== "module" you do not wrap originalScript in the initMap function (instead
inject loaderSnippet alongside originalScript or apply a module-compatible
injection), otherwise continue to wrap as before; update references to
loaderSnippet, originalScript and newScript accordingly and ensure module
scripts are emitted unchanged in scope.
♻️ Duplicate comments (1)
webpack-plugins/inject-leaflet-loader.js (1)
137-139: Rename shadowing variable to satisfy Biome lint.
constructorshadows the global identifier and tripslint/suspicious/noShadowRestrictedNames. This was already flagged earlier.🔧 Suggested fix
- const HtmlWebpackPlugin = compiler.options.plugins - .map((plugin) => plugin.constructor) - .find((constructor) => constructor && constructor.name === "HtmlWebpackPlugin"); + const HtmlWebpackPlugin = compiler.options.plugins + .map((plugin) => plugin.constructor) + .find((pluginCtor) => pluginCtor && pluginCtor.name === "HtmlWebpackPlugin");
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 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-formatcommand with Python virtualenv enabled andyarn lint:fix(runs eslint --fix and prettier via lint-staged)
Files:
webpack-plugins/inject-leaflet-loader.js
🧠 Learnings (4)
📓 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
🧬 Code graph analysis (1)
webpack-plugins/inject-leaflet-loader.js (1)
webpack.config.js (2)
InjectLeafletLoaderPlugin(10-10)HtmlWebpackPlugin(5-5)
🪛 Biome (2.1.2)
webpack-plugins/inject-leaflet-loader.js
[error] 139-139: 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 (2)
webpack-plugins/inject-leaflet-loader.js (2)
6-19: LGTM: constructor config is clear and scoped.
isEchartsOnlygating plus an explicitmapExampleslist keeps the plugin’s scope predictable.
22-128: LGTM: loader snippet covers Leaflet + plugin sequencing cleanly.The onload/error handling and plugin gating look solid for the echarts-only case.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| // Find the script tag and its content (handles <script>, <script type="text/javascript">, and <script type="module">) | ||
| const scriptRegex = | ||
| /<script(?:\s+type="(?:text\/javascript|module)")?>\s*([\s\S]*?)\s*<\/script>/i; | ||
| const match = html.match(scriptRegex); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Broaden inline-script matching to avoid missed injection.
The regex only matches <script> tags with an optional type and no other attributes. If the HTML adds defer, nonce, or other attrs, the match fails and Leaflet assets are removed without injecting the loader, breaking the page. Consider matching any inline script and preserving all attributes.
🔧 Suggested fix
- const scriptRegex =
- /<script(?:\s+type="(?:text\/javascript|module)")?>\s*([\s\S]*?)\s*<\/script>/i;
- const match = html.match(scriptRegex);
+ const scriptRegex =
+ /<script\b(?![^>]*\bsrc=)([^>]*)>\s*([\s\S]*?)\s*<\/script>/i;
+ const match = html.match(scriptRegex);
if (match) {
- const originalScript = match[1];
+ const originalAttrs = match[1] || "";
+ const originalScript = match[2];
const loaderSnippet = this.getLeafletLoaderSnippet(hasPlugins);
-
- // Determine the script type from the original tag
- const scriptTypeMatch = html.match(
- /<script(\s+type="(text\/javascript|module)")?>/i,
- );
- const scriptType =
- scriptTypeMatch && scriptTypeMatch[2]
- ? scriptTypeMatch[2]
- : "text/javascript";
// Wrap the original script in initMap function and add the loader
const newScript = `${loaderSnippet}\n${originalScript}\n }`;
html = html.replace(
scriptRegex,
- `<script type="${scriptType}">\n ${newScript}\n </script>`,
+ `<script${originalAttrs}>\n ${newScript}\n </script>`,
);
}Also applies to: 216-219
🤖 Prompt for AI Agents
In `@webpack-plugins/inject-leaflet-loader.js` around lines 195 - 199, The
inline-script regex is too restrictive (only allows an optional type attribute)
and misses scripts with other attrs (defer, nonce, etc.), causing the loader not
to be injected; update the matching logic around the scriptRegex variable to
match any <script ...> tag with arbitrary attributes but no src attribute (e.g.
capture attributes with /<script\b([^>]*?)>\s*([\s\S]*?)\s*<\/script>/i and then
ensure you only treat tags without a src attribute as inline), preserve the full
attributes string when reinserting the modified script, and apply the same
change to the other occurrence around lines 216-219 so all inline scripts are
detected and updated correctly.
| // Determine the script type from the original tag | ||
| const scriptTypeMatch = html.match( | ||
| /<script(\s+type="(text\/javascript|module)")?>/i, | ||
| ); | ||
| const scriptType = | ||
| scriptTypeMatch && scriptTypeMatch[2] | ||
| ? scriptTypeMatch[2] | ||
| : "text/javascript"; | ||
|
|
||
| // Wrap the original script in initMap function and add the loader | ||
| const newScript = `${loaderSnippet}\n${originalScript}\n }`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
map_examples=(
netjson-clustering.html
netjson-switchGraphMode.html
netjsonmap-appendData.html
netjsonmap-appendData2.html
netjsonmap-indoormap.html
netjsonmap-multipleTiles.html
netjsonmap-nodeTiles.html
netjsonmap-plugins.html
netjsonmap.html
njg-geojson.html
)
for f in "${map_examples[@]}"; do
path=$(fd -a -g "$f" || true)
if [[ -z "$path" ]]; then
echo "Not found: $f"
continue
fi
echo "== $path =="
rg -n '<script' "$path"
rg -n 'type="module"|import\s|export\s' "$path" || true
doneRepository: openwisp/netjsongraph.js
Length of output: 1380
🏁 Script executed:
cat -n /home/jailuser/git/public/example_templates/netjsonmap-plugins.html | sed -n '20,35p'Repository: openwisp/netjsongraph.js
Length of output: 680
🏁 Script executed:
cat -n /home/jailuser/git/public/example_templates/netjsonmap-plugins.html | sed -n '28,60p'Repository: openwisp/netjsongraph.js
Length of output: 1264
🏁 Script executed:
cat -n /home/jailuser/git/public/example_templates/netjsonmap-plugins.html | sed -n '28,100p'Repository: openwisp/netjsongraph.js
Length of output: 3096
🏁 Script executed:
cat -n /home/jailuser/git/public/example_templates/netjsonmap-plugins.html | grep -E 'import|export|</script>'Repository: openwisp/netjsongraph.js
Length of output: 259
🏁 Script executed:
cat -n /home/jailuser/git/webpack-plugins/inject-leaflet-loader.js | sed -n '195,225p'Repository: openwisp/netjsongraph.js
Length of output: 1432
Add guard to skip wrapping module scripts.
The code detects <script type="module"> (line 206) but still wraps the script content in the initMap function (line 214) regardless of type. The example file netjsonmap-plugins.html uses a module script, which will be incorrectly wrapped. Module scripts must not be nested inside functions as they have their own scope. Either skip wrapping for module scripts or use a different injection strategy for them.
🤖 Prompt for AI Agents
In `@webpack-plugins/inject-leaflet-loader.js` around lines 204 - 214, The current
logic detects module scripts via scriptTypeMatch/scriptType but still wraps
everything in an initMap function when creating newScript, which breaks module
semantics; modify the code around scriptTypeMatch/scriptType and the newScript
construction so that if scriptType === "module" you do not wrap originalScript
in the initMap function (instead inject loaderSnippet alongside originalScript
or apply a module-compatible injection), otherwise continue to wrap as before;
update references to loaderSnippet, originalScript and newScript accordingly and
ensure module scripts are emitted unchanged in scope.
Checklist
Reference to Existing Issue
Closes #392
Description of Changes
Use chunk splitting, dynamic loading and removed unused examples to reduce library size.
#425 (comment)