Skip to content

Commit 6b81f97

Browse files
ryan-williamsclaude
andcommitted
trim factory bundle: skip plot_api/index.js, replace events package
Two optimizations to the `createPlotly()` factory: 1. Import `plot_api/plot_api.js` directly instead of `plot_api/index.js`, avoiding transitive imports of `to_image.js`, `validate.js`, `template_api.js`, and `snapshot/download.js` (-17 KB min) 2. Replace Node.js `events` package (15 KB) with inline MiniEmitter (~50 lines) in `lib/events.js`. Only `on`/`once`/`emit`/ `removeListener`/`removeAllListeners` are used. (-5 KB min) Factory: 741 KB min (was 763 KB, -22 KB / -2.9%) vs upstream basic: -33% (was -30%) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 1fca144 commit 6b81f97

File tree

4 files changed

+82
-25
lines changed

4 files changed

+82
-25
lines changed
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"upstream_full__min_": 4831947,
33
"upstream_basic__min_": 1114816,
4-
"fork_full__min_": 4808668,
5-
"fork_basic__min_": 1095031,
6-
"fork_minimal__min_": 987325,
7-
"fork_lite__min_": 847320,
4+
"fork_full__min_": 4809524,
5+
"fork_basic__min_": 1095887,
6+
"fork_minimal__min_": 988182,
7+
"fork_lite__min_": 848177,
88
"app___upstream_basic": 1115573,
9-
"app___fork_basic": 1095179,
10-
"app___fork_lite": 847412,
11-
"fork_factory__min_": 781250,
12-
"app___fork_factory": 781250
9+
"app___fork_basic": 1096035,
10+
"app___fork_lite": 848269,
11+
"fork_factory__min_": 758490,
12+
"app___fork_factory": 758490
1313
}

perf/thresholds.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
{
22
"bundleSize": {
33
"lite": {
4-
"expected_bytes": 1801579
4+
"expected_bytes": 1803169
55
},
66
"lite-min": {
77
"expected_bytes": 916326
88
},
99
"minimal": {
10-
"expected_bytes": 2124345
10+
"expected_bytes": 2125952
1111
},
1212
"minimal-min": {
1313
"expected_bytes": 1076875
1414
},
1515
"basic": {
16-
"expected_bytes": 2429429
16+
"expected_bytes": 2431036
1717
},
1818
"basic-min": {
1919
"expected_bytes": 1185641

src/core-factory.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import './lib/d3-compat.js';
2020
import 'd3-transition';
2121
import 'native-promise-only';
2222
import Registry from './registry.js';
23-
import plotApi from './plot_api/index.js';
23+
// Import plot_api.js directly (not index.js) to avoid pulling in
24+
// to_image, validate, template_api, snapshot/download
25+
import main from './plot_api/plot_api.js';
2426
import FxModule from './components/fx/index.js';
2527
import PlotsModule from './plots/plots.js';
2628
import { version } from './version.js';
@@ -34,6 +36,14 @@ import modebar from './components/modebar/index.js';
3436
import localeEn from './locale-en.js';
3537
import localeEnUs from './locale-en-us.js';
3638

39+
// API methods needed for runtime (excludes toImage, validate, etc.)
40+
var apiMethodNames = [
41+
'newPlot', 'react', 'restyle', 'relayout', 'redraw', 'update',
42+
'extendTraces', 'prependTraces', 'addTraces', 'deleteTraces', 'moveTraces',
43+
'purge', 'addFrames', 'deleteFrames', 'animate', 'setPlotConfig',
44+
'_doPlot', '_guiRestyle', '_guiRelayout', '_guiUpdate', '_storeDirectGUIEdit',
45+
];
46+
3747
export function createPlotly({ traces = [], components = [], Icons, Snapshot, PlotSchema } = {}) {
3848
var register = Registry.register;
3949
var Plotly = { version, register };
@@ -42,11 +52,11 @@ export function createPlotly({ traces = [], components = [], Icons, Snapshot, Pl
4252
if(PlotSchema) Plotly.PlotSchema = PlotSchema;
4353

4454
// Register API methods
45-
var methodNames = Object.keys(plotApi);
46-
for(var i = 0; i < methodNames.length; i++) {
47-
var name = methodNames[i];
48-
if(name.charAt(0) !== '_') Plotly[name] = plotApi[name];
49-
register({ moduleType: 'apiMethod', name: name, fn: plotApi[name] });
55+
for(var i = 0; i < apiMethodNames.length; i++) {
56+
var name = apiMethodNames[i];
57+
if(!main[name]) continue;
58+
if(name.charAt(0) !== '_') Plotly[name] = main[name];
59+
register({ moduleType: 'apiMethod', name: name, fn: main[name] });
5060
}
5161

5262
// Register essential components (always needed)

src/lib/events.js

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,63 @@
1-
import { EventEmitter } from 'events';
1+
// Minimal EventEmitter (replaces Node.js 'events' package, ~15 KB)
2+
function MiniEmitter() {
3+
this._events = {};
4+
}
5+
MiniEmitter.prototype.on = function(event, fn) {
6+
var handlers = this._events[event];
7+
if(!handlers) this._events[event] = fn;
8+
else if(typeof handlers === 'function') this._events[event] = [handlers, fn];
9+
else handlers.push(fn);
10+
return this;
11+
};
12+
MiniEmitter.prototype.once = function(event, fn) {
13+
var self = this;
14+
function wrapper() {
15+
self.removeListener(event, wrapper);
16+
fn.apply(this, arguments);
17+
}
18+
wrapper.listener = fn;
19+
return this.on(event, wrapper);
20+
};
21+
MiniEmitter.prototype.removeListener = function(event, fn) {
22+
var handlers = this._events[event];
23+
if(!handlers) return this;
24+
if(typeof handlers === 'function') {
25+
if(handlers === fn || handlers.listener === fn) delete this._events[event];
26+
} else {
27+
for(var i = handlers.length - 1; i >= 0; i--) {
28+
if(handlers[i] === fn || handlers[i].listener === fn) {
29+
handlers.splice(i, 1);
30+
break;
31+
}
32+
}
33+
if(handlers.length === 0) delete this._events[event];
34+
else if(handlers.length === 1) this._events[event] = handlers[0];
35+
}
36+
return this;
37+
};
38+
MiniEmitter.prototype.removeAllListeners = function(event) {
39+
if(event) delete this._events[event];
40+
else this._events = {};
41+
return this;
42+
};
43+
MiniEmitter.prototype.emit = function(event, data) {
44+
var handlers = this._events[event];
45+
if(!handlers) return false;
46+
if(typeof handlers === 'function') handlers.call(this, data);
47+
else {
48+
var arr = handlers.slice();
49+
for(var i = 0; i < arr.length; i++) arr[i].call(this, data);
50+
}
51+
return true;
52+
};
253

354
var Events = {
455

556
init: function(plotObj) {
6-
/*
7-
* If we have already instantiated an emitter for this plot
8-
* return early.
9-
*/
10-
if(plotObj._ev instanceof EventEmitter) return plotObj;
57+
if(plotObj._ev instanceof MiniEmitter) return plotObj;
1158

12-
var ev = new EventEmitter();
13-
var internalEv = new EventEmitter();
59+
var ev = new MiniEmitter();
60+
var internalEv = new MiniEmitter();
1461

1562
/*
1663
* Assign to plot._ev while we still live in a land

0 commit comments

Comments
 (0)