From a3410797ed73d9d10943bd497c47f7f0ead4b5b2 Mon Sep 17 00:00:00 2001 From: Ian Webster Date: Thu, 15 Dec 2022 11:20:15 -0500 Subject: [PATCH] Add support for Chart.js v4 (#145) --- README.md | 4 ++-- lib/charts.js | 41 ++++++++++++++++++++++------------------- package.json | 3 ++- yarn.lock | 5 +++++ 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 77ab994..1078d37 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,9 @@ The chart configuration object is based on the popular Chart.js API. Check out QuickChart includes many Chart.js plugins that allow you to add chart annotations, data labels, and more: `chartjs-plugin-datalabels`, `chartjs-plugin-annotation`, `chartjs-plugin-piechart-outlabels`, `chartjs-chart-radial-gauge`, `chartjs-chart-box-and-violin-plot `, `chartjs-plugin-doughnutlabel`, and `chartjs-plugin-colorschemes`. -### Chart.js v3 +### Chart.js versions -Chart.js v3 is supported via the `version` parameter ([documentation](https://quickchart.io/documentation/) to read more about parameters). Custom chart plugins such as annotations and outlabels are disabled for >= 3.0.0 due to lack of support. +Chart.js v3 and v4 are supported via the `version` parameter ([documentation](https://quickchart.io/documentation/) to read more about parameters). Custom chart plugins such as annotations and outlabels currently not available for >= 3.0.0. Each QuickChart instance should use 1 specific version of the Chart.js library. Mixing and matching versions (e.g., rendering a v2 chart followed by a v3 chart) is not well supported. diff --git a/lib/charts.js b/lib/charts.js index b64f4fd..a8d765a 100644 --- a/lib/charts.js +++ b/lib/charts.js @@ -27,7 +27,17 @@ const MAX_WIDTH = process.env.CHART_MAX_WIDTH || 3000; const rendererCache = {}; -function getRenderer(width, height, version, format) { +async function getChartJsForVersion(version) { + if (version && version.startsWith('4')) { + return (await import('chart.js-v4/auto')).Chart; + } + if (version && version.startsWith('3')) { + return require('chart.js-v3'); + } + return require('chart.js'); +} + +async function getRenderer(width, height, version, format) { if (width > MAX_WIDTH) { throw `Requested width exceeds maximum of ${MAX_WIDTH}`; } @@ -37,9 +47,8 @@ function getRenderer(width, height, version, format) { const key = `${width}__${height}__${version}__${format}`; if (!rendererCache[key]) { - rendererCache[key] = new CanvasRenderService(width, height, undefined, format, () => { - return version.startsWith('3') ? require('chart.js-v3') : require('chart.js'); - }); + const Chart = await getChartJsForVersion(version); + rendererCache[key] = new CanvasRenderService(width, height, undefined, format, () => Chart); } return rendererCache[key]; } @@ -111,7 +120,7 @@ function patternDraw(shapeType, backgroundColor, patternColor, requestedSize) { return pattern.draw(shapeType, backgroundColor, patternColor, size); } -function renderChartJs( +async function renderChartJs( width, height, backgroundColor, @@ -135,7 +144,7 @@ function renderChartJs( pattern: { draw: patternDraw, }, - Chart: version.startsWith('3') ? require('chart.js-v3') : require('chart.js'), + Chart: getChartJsForVersion(version), }, }); chart = vm.run(`module.exports = ${untrustedChart}`); @@ -368,11 +377,11 @@ function renderChartJs( } logger.debug('Chart:', JSON.stringify(chart)); - if (version.startsWith('3')) { + if (version.startsWith('3') || version.startsWith('4')) { require('chartjs-adapter-moment'); } if (!chart.plugins) { - if (version.startsWith('3')) { + if (version.startsWith('3') || version.startsWith('4')) { chart.plugins = []; } else { const chartAnnotations = require('chartjs-plugin-annotation'); @@ -415,19 +424,13 @@ function renderChartJs( }); } - const canvasRenderService = getRenderer(width, height, version, format); + const canvasRenderService = await getRenderer(width, height, version, format); - try { - if (format === 'svg') { - // SVG rendering doesn't work asychronously. - return Promise.resolve(canvasRenderService.renderToBufferSync(chart, 'image/svg+xml')); - } - return canvasRenderService.renderToBuffer(chart); - } catch (err) { - // canvasRenderService doesn't seem to be throwing errors correctly for - // certain chart errors. - return Promise.reject(err.message || err); + if (format === 'svg') { + // SVG rendering doesn't work asychronously. + return canvasRenderService.renderToBufferSync(chart, 'image/svg+xml'); } + return canvasRenderService.renderToBuffer(chart); } module.exports = { diff --git a/package.json b/package.json index 9253598..a439987 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "quickchart", - "version": "1.7.1", + "version": "1.8.0", "main": "index.js", "license": "AGPL-3.0", "homepage": "https://quickchart.io/", @@ -26,6 +26,7 @@ "canvas-5-polyfill": "^0.1.5", "chart.js": "^2.9.4", "chart.js-v3": "npm:chart.js@3.9.1", + "chart.js-v4": "npm:chart.js@4.0.1", "chartjs-adapter-moment": "https://github.com/typpo/chartjs-adapter-moment.git#e9bc92ab6e0e500c91c4a9871db7b14d15b5c2e7", "chartjs-chart-box-and-violin-plot": "^2.4.0", "chartjs-chart-radial-gauge": "^1.0.3", diff --git a/yarn.lock b/yarn.lock index ee534a0..824d076 100644 --- a/yarn.lock +++ b/yarn.lock @@ -993,6 +993,11 @@ charenc@0.0.2: resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.9.1.tgz#3abf2c775169c4c71217a107163ac708515924b8" integrity sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w== +"chart.js-v4@npm:chart.js@4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.0.1.tgz#93d5d50ac222a5b3b6ac7488e82e1553ac031592" + integrity sha512-5/8/9eBivwBZK81mKvmIwTb2Pmw4D/5h1RK9fBWZLLZ8mCJ+kfYNmV9rMrGoa5Hgy2/wVDBMLSUDudul2/9ihA== + chart.js@^2.4.0, chart.js@^2.8.0, chart.js@^2.9.4: version "2.9.4" resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.9.4.tgz#0827f9563faffb2dc5c06562f8eb10337d5b9684"