mirror of
https://gitee.com/antv/g6.git
synced 2024-11-29 18:28:19 +08:00
feat: add g6 ssr
This commit is contained in:
parent
dbe0868027
commit
7f1dbd9f3c
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@ -18,6 +18,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: |
|
||||||
|
brew update
|
||||||
|
brew install python3 || : # python doesn't need to be linked
|
||||||
|
brew install pkg-config cairo pango libpng jpeg giflib librsvg
|
||||||
|
pip install setuptools
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
name: Install pnpm
|
name: Install pnpm
|
||||||
with:
|
with:
|
||||||
|
29
packages/g6-ssr/README.md
Normal file
29
packages/g6-ssr/README.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
## SSR extension for G6 5.0
|
||||||
|
|
||||||
|
This extension package provides SSR support for G6 5.0, which supports canvas rendering in server side.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @antv/g6-ssr
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Render
|
||||||
|
|
||||||
|
```js
|
||||||
|
// usage 1
|
||||||
|
import { createGraph } from '@antv/g6-ssr';
|
||||||
|
|
||||||
|
const graph = createGraph({
|
||||||
|
width: 500,
|
||||||
|
height: 500,
|
||||||
|
data: {
|
||||||
|
// your data
|
||||||
|
},
|
||||||
|
// your other options
|
||||||
|
});
|
||||||
|
|
||||||
|
graph.writeToFile('output.png');
|
||||||
|
```
|
BIN
packages/g6-ssr/__tests__/assets/file.pdf
Normal file
BIN
packages/g6-ssr/__tests__/assets/file.pdf
Normal file
Binary file not shown.
132
packages/g6-ssr/__tests__/assets/file.svg
Normal file
132
packages/g6-ssr/__tests__/assets/file.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 118 KiB |
BIN
packages/g6-ssr/__tests__/assets/image.png
Normal file
BIN
packages/g6-ssr/__tests__/assets/image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 114 KiB |
83
packages/g6-ssr/__tests__/test.spec.ts
Normal file
83
packages/g6-ssr/__tests__/test.spec.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { existsSync, readFileSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
import type { Graph } 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
return await createGraph({
|
||||||
|
width: 500,
|
||||||
|
height: 500,
|
||||||
|
outputType,
|
||||||
|
autoFit: 'view',
|
||||||
|
background: 'rgba(100, 80, 180, 0.4)',
|
||||||
|
data,
|
||||||
|
node: {
|
||||||
|
style: {
|
||||||
|
labelText: (d) => d.id,
|
||||||
|
labelFill: '#fff',
|
||||||
|
labelPlacement: 'center',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: 'circular',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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 () => {
|
||||||
|
const graph = await fn();
|
||||||
|
|
||||||
|
expect(graph).toMatchFile('./assets/image.png');
|
||||||
|
|
||||||
|
graph.writeToFile(join(__dirname, './assets/image'));
|
||||||
|
|
||||||
|
graph.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('file pdf', async () => {
|
||||||
|
const graph = await fn('pdf');
|
||||||
|
|
||||||
|
graph.writeToFile(join(__dirname, '/assets/file'));
|
||||||
|
|
||||||
|
graph.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('file svg', async () => {
|
||||||
|
const graph = await fn('svg');
|
||||||
|
|
||||||
|
expect(graph).toMatchFile('./assets/file.svg');
|
||||||
|
|
||||||
|
graph.writeToFile(join(__dirname, './assets/file'));
|
||||||
|
|
||||||
|
graph.destroy();
|
||||||
|
});
|
||||||
|
});
|
11
packages/g6-ssr/jest.config.js
Normal file
11
packages/g6-ssr/jest.config.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module.exports = {
|
||||||
|
transform: {
|
||||||
|
'^.+\\.[tj]s$': ['@swc/jest'],
|
||||||
|
},
|
||||||
|
collectCoverageFrom: ['src/**/*.ts'],
|
||||||
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
|
||||||
|
transformIgnorePatterns: [`<rootDir>/node_modules/.pnpm/(?!(d3-*))`],
|
||||||
|
moduleNameMapper: {
|
||||||
|
'@antv/g6': '<rootDir>/../g6/src',
|
||||||
|
},
|
||||||
|
};
|
36
packages/g6-ssr/package.json
Normal file
36
packages/g6-ssr/package.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "g6-ssr",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [
|
||||||
|
"antv",
|
||||||
|
"g6",
|
||||||
|
"ssr"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "Aarebecca",
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"module": "esm/index.js",
|
||||||
|
"types": "lib/index.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "run-p build:*",
|
||||||
|
"build:cjs": "rimraf ./lib && tsc --module commonjs --outDir lib -p tsconfig.build.json",
|
||||||
|
"build:esm": "rimraf ./esm && tsc --module ESNext --outDir esm -p tsconfig.build.json",
|
||||||
|
"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",
|
||||||
|
"type-check": "tsc --noEmit -p tsconfig.test.json"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@antv/g": "^6.1.3",
|
||||||
|
"@antv/g-canvas": "^2.0.12",
|
||||||
|
"@antv/g6": "workspace:*",
|
||||||
|
"canvas": "^2.11.2"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public",
|
||||||
|
"registry": "https://registry.npmjs.org/"
|
||||||
|
}
|
||||||
|
}
|
38
packages/g6-ssr/src/canvas.ts
Normal file
38
packages/g6-ssr/src/canvas.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { Renderer } from '@antv/g-canvas';
|
||||||
|
import { Canvas as G6Canvas } from '@antv/g6';
|
||||||
|
import type { Canvas as NodeCanvas } from 'canvas';
|
||||||
|
import { createCanvas as createNodeCanvas } from 'canvas';
|
||||||
|
import type { Options } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <zh/> 创建画布
|
||||||
|
*
|
||||||
|
* <en/> create canvas
|
||||||
|
* @param options <zh/> options 画布配置 | <en/> options canvas configuration
|
||||||
|
* @returns <zh/> [G6 画布, NodeCanvas 画布] | <en/> [G6Canvas, NodeCanvas]
|
||||||
|
*/
|
||||||
|
export function createCanvas(options: Options): [G6Canvas, NodeCanvas] {
|
||||||
|
const { width, height, background, outputType } = options;
|
||||||
|
const nodeCanvas = createNodeCanvas(width, height, outputType as any);
|
||||||
|
const offscreenNodeCanvas = createNodeCanvas(1, 1);
|
||||||
|
|
||||||
|
const g6Canvas = new G6Canvas({
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
background,
|
||||||
|
// @ts-expect-error missing types
|
||||||
|
canvas: nodeCanvas as any,
|
||||||
|
offscreenCanvas: offscreenNodeCanvas as any,
|
||||||
|
enableMultiLayer: false,
|
||||||
|
renderer: () => {
|
||||||
|
const renderer = new Renderer();
|
||||||
|
const htmlRendererPlugin = renderer.getPlugin('html-renderer');
|
||||||
|
const domInteractionPlugin = renderer.getPlugin('dom-interaction');
|
||||||
|
renderer.unregisterPlugin(htmlRendererPlugin);
|
||||||
|
renderer.unregisterPlugin(domInteractionPlugin);
|
||||||
|
return renderer;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return [g6Canvas, nodeCanvas];
|
||||||
|
}
|
54
packages/g6-ssr/src/graph.ts
Normal file
54
packages/g6-ssr/src/graph.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { Graph as G6Graph } from '@antv/g6';
|
||||||
|
import { existsSync, lstatSync, writeFileSync } from 'fs';
|
||||||
|
import { createCanvas } from './canvas';
|
||||||
|
import type { Graph, Options } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <zh/> 获取输出文件的扩展名
|
||||||
|
*
|
||||||
|
* <en/> Get the extension name of the output file
|
||||||
|
* @param options - <zh/>配置项 | <en/>options
|
||||||
|
* @returns - <zh/>输出文件的扩展名 | <en/>The extension name of the output file
|
||||||
|
*/
|
||||||
|
function getExtendNameOf(options: Options) {
|
||||||
|
const { outputType } = options;
|
||||||
|
if (outputType === 'pdf') return '.pdf';
|
||||||
|
if (outputType === 'svg') return '.svg';
|
||||||
|
return '.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <zh/> 创建图并等待渲染完成
|
||||||
|
*
|
||||||
|
* <en/> Create a graph and wait for the rendering to complete
|
||||||
|
* @param options - <zh/>图配置项 | <en/>Graph options
|
||||||
|
* @returns - <zh/>扩展图实例 | <en/>Extended graph instance
|
||||||
|
*/
|
||||||
|
export async function createGraph(options: Options) {
|
||||||
|
const [g6Canvas, nodeCanvas] = createCanvas(options);
|
||||||
|
|
||||||
|
const { outputType, ...restOptions } = options;
|
||||||
|
const graph = new G6Graph({
|
||||||
|
...restOptions,
|
||||||
|
container: g6Canvas,
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-expect-error extend Graph
|
||||||
|
graph.writeToFile = (file: string) => {
|
||||||
|
const extendName = getExtendNameOf(options);
|
||||||
|
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 extend Graph
|
||||||
|
graph.toBuffer = () => nodeCanvas.toBuffer();
|
||||||
|
|
||||||
|
await graph.render();
|
||||||
|
|
||||||
|
return graph as Graph;
|
||||||
|
}
|
3
packages/g6-ssr/src/index.ts
Normal file
3
packages/g6-ssr/src/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export { createCanvas } from './canvas';
|
||||||
|
export { createGraph } from './graph';
|
||||||
|
export type { Graph, Options } from './types';
|
19
packages/g6-ssr/src/types.ts
Normal file
19
packages/g6-ssr/src/types.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import type { GraphOptions } from '@antv/g6';
|
||||||
|
import { Graph as G6Graph } from '@antv/g6';
|
||||||
|
|
||||||
|
export interface Options extends Omit<GraphOptions, 'renderer' | 'container'> {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
/**
|
||||||
|
* <zh/> 输出文件类型,默认导出为图片
|
||||||
|
*
|
||||||
|
* <en/> output file type, default export as image
|
||||||
|
* @defaultValue 'image'
|
||||||
|
*/
|
||||||
|
outputType?: 'image' | 'pdf' | 'svg';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Graph extends G6Graph {
|
||||||
|
writeToFile: (file: string) => void;
|
||||||
|
toBuffer: () => Buffer;
|
||||||
|
}
|
7
packages/g6-ssr/tsconfig.build.json
Normal file
7
packages/g6-ssr/tsconfig.build.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"extends": "./tsconfig.json"
|
||||||
|
}
|
13
packages/g6-ssr/tsconfig.json
Normal file
13
packages/g6-ssr/tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"outDir": "lib",
|
||||||
|
"target": "ESNext",
|
||||||
|
"lib": ["ESNext"],
|
||||||
|
"paths": {
|
||||||
|
"@antv/g6": ["../g6/src/index.ts"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"include": ["src/**/*", "__tests__/**/*"]
|
||||||
|
}
|
7
packages/g6-ssr/tsconfig.test.json
Normal file
7
packages/g6-ssr/tsconfig.test.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*", "__tests__/**/*"],
|
||||||
|
"extends": "./tsconfig.json"
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user