Skip to content

Commit

Permalink
chartjs
Browse files Browse the repository at this point in the history
MARCROCK22 committed Aug 23, 2024

Verified

This commit was signed with the committer’s verified signature.
achingbrain Alex Potsides
1 parent d1035a1 commit 7a72784
Showing 9 changed files with 348 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/chartjs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fork of https://github.com/SeanSobey/ChartjsNodeCanvas but using @napi-rs/canvas
29 changes: 29 additions & 0 deletions packages/chartjs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "@slipher/chartjs",
"version": "0.0.1",
"private": false,
"license": "MIT",
"publishConfig": {
"access": "public"
},
"files": [
"lib/**"
],
"main": "./lib/index.js",
"module": "./lib/index.js",
"types": "./lib/index.d.ts",
"scripts": {
"dev": "tsc --watch",
"build": "tsc",
"lint": "biome lint --write ./src",
"format": "biome format --write ./src"
},
"devDependencies": {
"@types/node": "^22.3.0",
"typescript": "^5.5.4"
},
"dependencies": {
"@napi-rs/canvas": "^0.1.54",
"chart.js": "^4.4.4"
}
}
19 changes: 19 additions & 0 deletions packages/chartjs/src/backgroundcolorplugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Plugin as ChartJSPlugin, Chart as ChartJS } from 'chart.js';

export class BackgroundColourPlugin implements ChartJSPlugin {
public readonly id: string = 'chartjs-plugin-chartjs-napirs-canvas-background-colour';

public constructor(
private readonly _width: number,
private readonly _height: number,
private readonly _fillStyle: string,
) {}

public beforeDraw(chart: ChartJS): boolean | void {
const ctx = chart.ctx;
ctx.save();
ctx.fillStyle = this._fillStyle;
ctx.fillRect(0, 0, this._width, this._height);
ctx.restore();
}
}
8 changes: 8 additions & 0 deletions packages/chartjs/src/freshRequire.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const freshRequire: (id: string) => any = file => {
const resolvedFile = require.resolve(file);
const temp = require.cache[resolvedFile];
delete require.cache[resolvedFile];
const modified = require(resolvedFile);
require.cache[resolvedFile] = temp;
return modified;
};
1 change: 1 addition & 0 deletions packages/chartjs/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './napichart';
76 changes: 76 additions & 0 deletions packages/chartjs/src/napichart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type { ChartConfiguration, Chart as ChartJS } from 'chart.js/auto';
import { Canvas, createCanvas } from '@napi-rs/canvas';
import { freshRequire } from './freshRequire';
import { BackgroundColourPlugin } from './backgroundcolorplugin';
import type { ChartJSNapiRSCanvasOptions } from './options';

export class NapiChartjsCanvas {
_chartJs: typeof ChartJS;

constructor(public options: ChartJSNapiRSCanvasOptions) {
this._chartJs = this.initialize(options);
}

private initialize(options: ChartJSNapiRSCanvasOptions): typeof ChartJS {
const chartJs: typeof ChartJS = require('chart.js/auto');

if (options.plugins?.requireChartJSLegacy) {
for (const plugin of options.plugins.requireChartJSLegacy) {
freshRequire(plugin);
delete require.cache[require.resolve(plugin)];
}
}

if (options.plugins?.globalVariableLegacy) {
(global as any).Chart = chartJs;
for (const plugin of options.plugins.globalVariableLegacy) {
freshRequire(plugin);
}
delete (global as any).Chart;
}

if (options.plugins?.modern) {
for (const plugin of options.plugins.modern) {
if (typeof plugin === 'string') {
chartJs.register(freshRequire(plugin));
} else {
chartJs.register(plugin);
}
}
}

if (options.plugins?.requireLegacy) {
for (const plugin of options.plugins.requireLegacy) {
chartJs.register(freshRequire(plugin));
}
}

if (options.chartCallback) {
options.chartCallback(chartJs);
}

if (options.backgroundColour) {
chartJs.register(new BackgroundColourPlugin(options.width, options.height, options.backgroundColour));
}

delete require.cache[require.resolve('chart.js')];

return chartJs;
}

renderChart(configuration: ChartConfiguration) {
const canvas = createCanvas(this.options.width, this.options.height);
configuration.options ??= {};
configuration.options.responsive = false;
configuration.options.animation = false;
return new this._chartJs(canvas.getContext('2d') as any, configuration) as any as Omit<
ChartJS,
'canvas'
> & { canvas: Canvas };
}

renderToBuffer(configuration: ChartConfiguration) {
const chart = this.renderChart(configuration);
return (chart.canvas as any as Canvas).toBuffer('image/png');
}
}
50 changes: 50 additions & 0 deletions packages/chartjs/src/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { Chart as ChartJS, ChartComponentLike } from 'chart.js';

export type ChartCallback = (chartJS: typeof ChartJS) => void | Promise<void>;

export interface ChartJSNapiRSCanvasOptions {
/**
* The width of the charts to render, in pixels.
*/
readonly width: number;
/**
* The height of the charts to render, in pixels.
*/
readonly height: number;
/**
* Optional callback which is called once with a new ChartJS global reference as the only parameter.
*/
readonly chartCallback?: ChartCallback;
/**
* Optional canvas type ('PDF' or 'SVG'), see the [canvas pdf doc](https://github.com/Automattic/node-canvas#pdf-output-support).
*/
readonly type?: 'pdf' | 'svg';
/**
* Optional plugins to register.
*/
readonly plugins?: ChartJSNapiRSCanvasPlugins;

/**
* Optional background color for the chart, otherwise it will be transparent. Note, this will apply to all charts. See the [fillStyle](https://www.w3schools.com/tags/canvas_fillstyle.asp) canvas API used for possible values.
*/
readonly backgroundColour?: string;
}

export type ChartJSNapiRSCanvasPlugins = {
/**
* Global plugins, see https://www.chartjs.org/docs/latest/developers/plugins.html.
*/
readonly modern?: ReadonlyArray<string | ChartComponentLike>;
/**
* This will work for plugins that `require` ChartJS themselves.
*/
readonly requireChartJSLegacy?: ReadonlyArray<string>;
/**
* This should work for any plugin that expects a global Chart variable.
*/
readonly globalVariableLegacy?: ReadonlyArray<string>;
/**
* This will work with plugins that just return a plugin object and do no specific loading themselves.
*/
readonly requireLegacy?: ReadonlyArray<string>;
};
38 changes: 38 additions & 0 deletions packages/chartjs/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"compilerOptions": {
"module": "CommonJS",
"target": "ESNext",
"lib": [
"ESNext",
"WebWorker"
],
"moduleResolution": "node",
"declaration": true,
"sourceMap": false,
"strict": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"preserveConstEnums": true,
/* Type Checking */
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"noErrorTruncation": true,
"outDir": "./lib",
"stripInternal": true,
},
"exclude": [
"**/lib",
"**/__test__"
],
"include": [
"src"
]
}
126 changes: 126 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 7a72784

Please sign in to comment.