Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added packages/g6-ssr/__tests__/assets/bin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/g6-ssr/__tests__/assets/file.pdf
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,17 +1,35 @@
import { existsSync, readFileSync } from 'fs';
import { join } from 'path';
import type { Graph } from '../src';
import type { Graph, MetaData } from '../src';
import { createGraph } from '../src';

declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace jest {
interface Matchers<R> {
toMatchFile(path: string): R;
toMatchFile(path: string, meta?: MetaData): R;
}
}
}

expect.extend({
toMatchFile: (received: Graph, path: string, meta?: MetaData) => {
const _path = join(__dirname, path);
const pass = existsSync(_path) ? received.toBuffer(meta).equals(readFileSync(_path)) : true;
if (pass) {
return {
message: () => 'passed',
pass: true,
};
} else {
return {
message: () => 'expected files are equal',
pass: false,
};
}
},
});

describe('createGraph', () => {
const fn = async (outputType?: any) => {
const data = (await fetch('https://assets.antv.antgroup.com/g6/circular.json').then((res) => res.json())) as any;
Expand All @@ -36,24 +54,7 @@ describe('createGraph', () => {
});
};

expect.extend({
toMatchFile: (received: Graph, path: string) => {
const pass = existsSync(path) ? received.toBuffer().equals(readFileSync(path)) : true;
if (pass) {
return {
message: () => 'passed',
pass: true,
};
} else {
return {
message: () => 'expected files are equal',
pass: false,
};
}
},
});

it('image image', async () => {
it('image png', async () => {
const graph = await fn();

expect(graph).toMatchFile('./assets/image.png');
Expand All @@ -66,7 +67,19 @@ describe('createGraph', () => {
it('file pdf', async () => {
const graph = await fn('pdf');

graph.exportToFile(join(__dirname, '/assets/file'));
const metadata = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里 pdf 添加 metadata,避免每次测试生成的文件不一致

title: 'Chart',
author: 'AntV',
creator: 'Aaron',
subject: 'Test',
keywords: 'antv g2 chart pdf',
creationDate: new Date(1730304000000), // 2024-10-31 00:00:00 UTC+8
modDate: new Date(1730304000000), // 2024-10-31 00:00:00 UTC+8
};

expect(graph).toMatchFile('./assets/file.pdf', metadata);

graph.exportToFile(join(__dirname, '/assets/file'), metadata);

graph.destroy();
});
Expand Down
12 changes: 6 additions & 6 deletions packages/g6-ssr/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@antv/g6-ssr",
"version": "0.0.1",
"description": "",
"description": "Support SSR for G6",
"keywords": [
"antv",
"g6",
Expand All @@ -19,19 +19,19 @@
"scripts": {
"build": "rimraf ./dist && rollup -c",
"ci": "run-s lint type-check build test",
"dev": "tsx ./src/index.ts",
"lint": "eslint ./src __tests__ --quiet && prettier ./src __tests__ --check",
"prepublishOnly": "npm run ci",
"test": "jest",
"test": "run-s test:*",
"test:bin": "node ./bin/g6-ssr.js export -i ./__tests__/graph-options.json -o __tests__/assets/bin",
"test:unit": "jest",
"type-check": "tsc --noEmit -p tsconfig.test.json"
},
"dependencies": {
"@antv/g": "^6.1.3",
"@antv/g-canvas": "^2.0.12",
"@antv/g6": "5.0.28-alpha.1",
"@antv/g6": "workspace:*",
"cac": "^6.7.14",
"canvas": "^2.11.2",
"webpack-cli": "^5.1.4"
"canvas": "^2.11.2"
},
"publishConfig": {
"access": "public",
Expand Down
2 changes: 0 additions & 2 deletions packages/g6-ssr/rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ export default [
file: 'dist/g6-ssr.cjs',
format: 'cjs',
exports: 'named',
// format: 'umd',
// name: 'G6SSR',
sourcemap: true,
},
plugins: [
Expand Down
30 changes: 18 additions & 12 deletions packages/g6-ssr/src/graph.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Graph as G6Graph } from '@antv/g6';
import { existsSync, lstatSync, writeFileSync } from 'fs';
import { createCanvas } from './canvas';
import type { Graph, Options } from './types';
import type { Graph, MetaData, Options } from './types';

/**
* <zh/> 获取输出文件的扩展名
Expand All @@ -10,13 +10,15 @@ import type { Graph, Options } from './types';
* @param options - <zh/>配置项 | <en/>options
* @returns <zh/>输出文件的扩展名 | <en/>The extension name of the output file
*/
function getExtendNameOf(options: Options) {
function getInfoOf(options: Options) {
const { outputType } = options;
if (outputType === 'pdf') return '.pdf';
if (outputType === 'svg') return '.svg';
return '.png';
if (outputType === 'pdf') return ['.pdf', 'application/pdf'] as const;
if (outputType === 'svg') return ['.svg', undefined] as const;
return ['.png', 'image/png'] as const;
}

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

/**
* <zh/> 创建图并等待渲染完成
*
Expand All @@ -27,29 +29,33 @@ function getExtendNameOf(options: Options) {
export async function createGraph(options: Options) {
const [g6Canvas, nodeCanvas] = createCanvas(options);

const { outputType, ...restOptions } = options;
const { outputType, waitForRender = 16, ...restOptions } = options;
const graph = new G6Graph({
animation: false,
...restOptions,
container: g6Canvas,
});

const [extendName, mimeType] = getInfoOf(options);

await graph.render();

await sleep(waitForRender); // wait for the rendering to complete

// @ts-expect-error extend Graph
graph.exportToFile = (file: string) => {
const extendName = getExtendNameOf(options);
graph.exportToFile = (file: string, meta?: MetaData) => {
if (!file.endsWith(extendName)) {
if (!existsSync(file)) file += extendName;
else if (lstatSync(file).isDirectory()) file = `${file}/image${extendName}`;
else file += extendName;
}

writeFileSync(file, nodeCanvas.toBuffer());
// @ts-expect-error skip type check
writeFileSync(file, nodeCanvas.toBuffer(mimeType, meta));
};

// @ts-expect-error extend Graph
graph.toBuffer = () => nodeCanvas.toBuffer();

await graph.render();
graph.toBuffer = (meta?: MetaData) => nodeCanvas.toBuffer(mimeType, meta);

return graph as Graph;
}
2 changes: 1 addition & 1 deletion packages/g6-ssr/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { createCanvas } from './canvas';
export { createGraph } from './graph';
export type { Graph, Options } from './types';
export type { Graph, MetaData, Options } from './types';
14 changes: 12 additions & 2 deletions packages/g6-ssr/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import type { GraphOptions } from '@antv/g6';
import { Graph as G6Graph } from '@antv/g6';
import type { PdfConfig, PngConfig } from 'canvas';

export interface Options extends Omit<GraphOptions, 'renderer' | 'container'> {
width: number;
height: number;
/**
* <zh/> 等待渲染的时间,默认为 16ms
*
* <en/> The time to wait for rendering, default is 16ms
* @defaultValue 16
*/
waitForRender?: number;
/**
* <zh/> 输出文件类型,默认导出为图片
*
Expand All @@ -13,7 +21,9 @@ export interface Options extends Omit<GraphOptions, 'renderer' | 'container'> {
outputType?: 'image' | 'pdf' | 'svg';
}

export type MetaData = PdfConfig | PngConfig;

export interface Graph extends G6Graph {
exportToFile: (file: string) => void;
toBuffer: () => Buffer;
exportToFile: (file: string, meta?: MetaData) => void;
toBuffer: (meta?: MetaData) => Buffer;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个 api 的作用是什么呀

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

如果下游用户有需求直接拿到导出图片的 buffer,而不是写入到本地文件,可以调这个 API。
比如测试中,可以通过 toBuffer 拿到新生成的文件,并与本地文件进行比较,而不需要先将新截图写入。

}
Loading