Source code:

var wgPageName = mw.config.get('wgPageName');
var wgCurRevisionId = mw.config.get('wgCurRevisionId');
// var wgAction = mw.config.get('wgAction');
var wgAction = new URLSearchParams(location.href).get("action"); // detect fake actions

function HistoryGraph() {
	if(wgCurRevisionId == 0) return;
	var hist = document.getElementById("ca-history");
	if(!hist) return;
	mw.util.addPortletLink('p-cactions', `/w/index.php?title=${wgPageName}&action=graph`, 'Graph',
		'ca-graph', "View graph of page history", null, "#ca-move");
	if(wgAction != "graph") return;
	var contentDiv = document.getElementById("mw-content-text");
	contentDiv.innerHTML = '';
	var fakeTitle = wgPageName.replace(/_/g, " ") + ": History graph";
	document.getElementById("firstHeading").innerHTML = fakeTitle;
	document.title = fakeTitle;
	
	var ym = new Date().toJSON().match(/^(\d+)\-(\d+)/);
	var yyyy = parseInt(ym[1]), mm = parseInt(ym[2]), d1 = "01T00:00:00Z";
	var f = function(d) { return d.toString().padStart(2,'0'); }
	var url_base_dbg = '/w/api.php?action=query&prop=revisions&rvlimit=1&rvprop=size|timestamp|ids';
	var url_base = '/w/api.php?action=query&prop=revisions&rvlimit=1&rvprop=size&format=json';
	var f1 = function(r1) { return r1.json(); };
	var sz = [];
	var doneLoading = function() {
		var divs = contentDiv.getElementsByTagName("div");
		divs[divs.length-1].innerHTML += " (No more found!)";
		sz.push(0);
		drawGraph();
		}
	var loop = function() {
		var time = [f(yyyy), f(mm), d1].join('-');
		var url_i = `&titles=${wgPageName}&rvstart=${time}`;
		var url = url_base + url_i;
		var dbg_url = url_base_dbg + url_i;
		contentDiv.innerHTML += `<div><a href="${dbg_url}">Fetching last edit before ${time}...</a></div>`;
		var f2 = function(r2) {
			var jtxt = JSON.stringify(r2.query);
			var ms = jtxt.match(/"size"\:(\d+)/);
			if(!ms) { doneLoading(); return; }
			sz.push(parseInt(ms[1]));
			mm--;
			if(mm==0) { mm = 12; yyyy--; }
			loop();
			};
		fetch(url).then(f1).then(f2);
		};
	loop();
	var drawGraph = function() {
		var height = 480, color = "red", stroke_width = 2;
		var q = "JFMAMJJASOND";
		sz.reverse();
		var max = Math.max.apply(null, sz);
		var dl = Math.pow(10, Math.floor(Math.log10(max)-0.5));
		var dx = 20;
		var dy = height/max;
		var lmax = Math.floor(max/dl)*dl;
		var width = dx * (sz.length + 1);
		var pts = [];
		var vpath=[], vtext = [], ytext = [];
		for(var i = 0; i < sz.length; i++) {
			pts.push(`${(i * dx)}, ${height - (sz[i] * dy)}`);
			var x = i * dx;
			var h = (mm==1)? (height+40) : height;
			vpath.push(`<path d="M${x},0 L${x},${h}" />`);
			vtext.push(`<text x="${x}" y="${height+20}">${q[mm-1]}</text>`);
			if(i == 0 || mm == 1) 
				ytext.push(`<text x="${x}" y="${height+40}">${yyyy}</text>`);
			mm++;
			if(mm > 12) { mm = 1; yyyy++; }
			}
		var hpath = [], htext = [];
		for(var i = 0; i <= lmax; i += dl) {
			var y = height - (i * dy);
			hpath.push(`<path d="M0,${y} L${width},${y}" />`);
			htext.push(`<text x="40" y="${y}">${i}</text>`);
			}
		height += 60;
		var bkg = `<rect x="0" y="0" width="${width}" height="${height}" fill="white" />`;
		var path = `<path d="M${pts.join(' L')}" stroke="${color}" stroke-width="${stroke_width}" fill="none" />`;
		var g1 = `<g stroke="gray" stroke-width="1" fill="none">${hpath.join('')}${vpath.join('')}</g>`;
		var g2 = `<g font-family="monospace" font-size="12px" text-align="right" color="black">${htext.join('')}${vtext.join('')}${ytext.join('')}</g>`;
		var svg = `<svg width="${width}" height="${height}">${bkg}${g1}${path}${g2}</svg>`;
		contentDiv.innerHTML = svg;
		var blobUrl = URL.createObjectURL(new Blob([svg], {type:"image/svg+xml;charset=utf-8"}));
		contentDiv.innerHTML += `<a href="${blobUrl}" download="HistoryGraph-${wgPageName}.svg"><button>Download</button></a>`;
		}
	}

HistoryGraph();

For background, details, and caveats of this script, please read: