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
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { createGraphic } from '@visactor/vrender-core/graphic/creator';
import { registerGroupGraphic } from '@visactor/vrender-core/register/register-group';
import { GraphicStateExtension } from '../../src/state/graphic-extension';
import { AnimationStateManager } from '../../src/state/animation-state';
import { registerAnimate } from '../../src/register';

describe('GraphicStateExtension', () => {
afterEach(() => {
Expand Down Expand Up @@ -71,4 +74,14 @@ describe('GraphicStateExtension', () => {
expect(graphic.currentStates).toEqual(['hover']);
expect(graphic.normalAttrs).toEqual({ fill: 'blue' });
});

test('registerAnimate should mix animation APIs into graphics created through core creator subpath', () => {
registerAnimate();
registerGroupGraphic();

const group = createGraphic('group', {});

expect(typeof (group as any).applyAnimationState).toBe('function');
expect(typeof (group as any).clearAnimationStates).toBe('function');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ describe('contribution-provider', () => {
expect(spy2).toHaveBeenCalledTimes(1);
});

test('ContributionStore should use realm-level shared state for duplicated ESM entry evaluation', () => {
const id = Symbol('shared-svc');
const cache = createProviderCache({ isBound: jest.fn(() => false), getAll: jest.fn(() => []) }, id);
const state = (globalThis as any)[Symbol.for('@visactor/vrender-core/contribution-store-state')];

expect(state).toBeDefined();
expect(state.store).toBe(ContributionStore.store);
expect(state.store.get(id)?.has(cache)).toBe(true);
});

test('bindContributionProviderNoSingletonScope does not call inSingletonScope', () => {
let factory: DynamicValueFactory | undefined;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { application } from '../../../src/application';
import { AppContext, configureRuntimeApplicationForApp } from '../../../src/entries';
import { AppContext, configureRuntimeApplicationForApp, getRuntimeInstallerBindingContext } from '../../../src/entries';
import { DefaultIncrementalDrawContribution } from '../../../src/render/contributions/render/incremental-draw-contribution';

describe('runtime installer', () => {
test('runtime installer should use realm-level shared state for duplicated ESM entry evaluation', () => {
const state = (globalThis as any)[Symbol.for('@visactor/vrender-core/runtime-installer-state')];

expect(state).toBeDefined();
expect(state.runtimeInstallerContext).toBe(getRuntimeInstallerBindingContext());
});

test('configureRuntimeApplicationForApp should configure an app-scoped incremental draw contribution factory', () => {
const context = new AppContext();
const app = {
Expand Down
13 changes: 12 additions & 1 deletion packages/vrender-core/__tests__/unit/factory/factory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
IWindow
} from '../../../src/interface';
import type { IGraphic, IGraphicAttribute } from '../../../src/interface/graphic';
import { GraphicFactory, LayerFactory, StageFactory } from '../../../src/factory';
import { Factory, GraphicFactory, LayerFactory, StageFactory } from '../../../src/factory';

class StageStub {
readonly params: Partial<IStageParams>;
Expand Down Expand Up @@ -41,6 +41,17 @@ class GraphicStub {
}

describe('factory module', () => {
test('Factory plugin registry should use realm-level shared state for duplicated ESM entry evaluation', () => {
class PluginStub {}

Factory.registerPlugin('unit-plugin', PluginStub);
const state = (globalThis as any)[Symbol.for('@visactor/vrender-core/factory-state')];

expect(state).toBeDefined();
expect(state.pluginClasses['unit-plugin']).toBe(PluginStub);
expect(Factory.getPlugin('unit-plugin')).toBe(PluginStub);
});

test('StageFactory should create stage instances with forwarded params', () => {
const factory = new StageFactory(StageStub as any);
const params = { width: 320, height: 180 };
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
import type { IRectGraphicAttribute } from '../../../src/interface';
import { createGraphic, graphicCreator, registerGraphic } from '../../../src/graphic';
import { Arc3d } from '../../../src/graphic/arc3d';
import { Graphic } from '../../../src/graphic/base';
import { DefaultGraphicService } from '../../../src/graphic/graphic-service/graphic-service';
import { Group } from '../../../src/graphic/group';
import { Rect } from '../../../src/graphic/rect';
import { registerArc3dGraphic } from '../../../src/register/register-arc3d';
import { registerGroupGraphic } from '../../../src/register/register-group';
import { registerRectGraphic } from '../../../src/register/register-rect';

class GraphicStub {
constructor(public readonly attribute: Record<string, unknown>) {}
}

describe('graphic factory migration', () => {
test('graphic registry should use realm-level shared state for duplicated ESM entry evaluation', () => {
registerGraphic('realm-shared-stub', GraphicStub as any);

const registryState = (globalThis as any)[Symbol.for('@visactor/vrender-core/graphic-registry')];

expect(registryState).toBeDefined();
expect(registryState.graphicCreator).toBe(graphicCreator);
expect(registryState.graphicFactory.create('realm-shared-stub', { x: 1 })).toBeInstanceOf(GraphicStub);
expect(createGraphic('realm-shared-stub', { x: 2 })).toBeInstanceOf(GraphicStub);
});

test('Graphic class should use realm-level shared state for duplicated ESM entry evaluation', () => {
registerGroupGraphic();
registerArc3dGraphic();

const classState = (globalThis as any)[Symbol.for('@visactor/vrender-core/graphic-class')];
const group = createGraphic('group', {});
const arc3d = createGraphic('arc3d', {});

expect(classState).toBeDefined();
expect(classState.Graphic).toBe(Graphic);
expect(group).toBeInstanceOf(classState.Graphic);
expect(arc3d).toBeInstanceOf(classState.Graphic);
});

test('registerGraphic should register creators for createGraphic', () => {
registerGraphic('unit-stub', GraphicStub as any);

Expand Down Expand Up @@ -37,6 +67,22 @@ describe('graphic factory migration', () => {
expect((rect as Rect).attribute.width).toBe(100);
});

test('registerGroupGraphic should enable createGraphic for group from a separate register entry', () => {
registerGroupGraphic();

const group = createGraphic('group', {});

expect(group).toBeInstanceOf(Group);
});

test('registerArc3dGraphic should enable createGraphic for arc3d from a separate register entry', () => {
registerArc3dGraphic();

const arc3d = createGraphic('arc3d', {});

expect(arc3d).toBeInstanceOf(Arc3d);
});

test('DefaultGraphicService should default to the shared graphic creator adapter', () => {
const service = new DefaultGraphicService();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,14 @@ describe('legacy binding context', () => {
expect(loadRenderContributions).toHaveBeenCalledTimes(1);
});
});

test('legacy bootstrap should use realm-level shared state for duplicated ESM entry evaluation', () => {
jest.isolateModules(() => {
const bootstrap = require('../../../src/legacy/bootstrap');
const state = (globalThis as any)[Symbol.for('@visactor/vrender-core/legacy-bootstrap-state')];

expect(state).toBeDefined();
expect(state.legacyBindingContext).toBe(bootstrap.getLegacyBindingContext());
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ function readArtifact(relativePath: string) {
}

describe('vrender-core container compatibility', () => {
test('application should use realm-level shared state for duplicated ESM entry evaluation', () => {
const { application } = require(path.join(packageRoot, 'src/application'));
const state = (globalThis as any)[Symbol.for('@visactor/vrender-core/application-state')];

expect(state).toBeDefined();
expect(state.application).toBe(application);
});

test('es artifacts should expose legacy container compatibility surface', () => {
expect(readArtifact('es/modules.js')).toContain('export const container');
expect(readArtifact('es/modules.d.ts')).toContain('container');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type ExpectedSubpath = {

const expectedExports: ExpectedSubpath[] = [
{ subpath: './graphic/creator', source: 'src/graphic/creator.ts' },
{ subpath: './graphic/graphic-registry', source: 'src/graphic/graphic-registry.ts' },
{ subpath: './graphic/base', source: 'src/graphic/base.ts' },
{ subpath: './graphic/modules', source: 'src/graphic/modules.ts' },
{ subpath: './graphic/symbol', source: 'src/graphic/symbol.ts' },
Expand Down
6 changes: 6 additions & 0 deletions packages/vrender-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"graphic/base": ["es/graphic/base.d.ts"],
"graphic/builtin-symbol": ["es/graphic/builtin-symbol.d.ts"],
"graphic/creator": ["es/graphic/creator.d.ts"],
"graphic/graphic-registry": ["es/graphic/graphic-registry.d.ts"],
"graphic/group": ["es/graphic/group.d.ts"],
"graphic/modules": ["es/graphic/modules.d.ts"],
"graphic/symbol": ["es/graphic/symbol.d.ts"],
Expand Down Expand Up @@ -185,6 +186,11 @@
"import": "./es/graphic/creator.js",
"require": "./cjs/graphic/creator.js"
},
"./graphic/graphic-registry": {
"types": "./es/graphic/graphic-registry.d.ts",
"import": "./es/graphic/graphic-registry.js",
"require": "./cjs/graphic/graphic-registry.js"
},
"./graphic/group": {
"types": "./es/graphic/group.d.ts",
"import": "./es/graphic/group.js",
Expand Down
58 changes: 58 additions & 0 deletions packages/vrender-core/src/application-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { IGraphicUtil, ILayerService, ITransformUtil } from './interface/core';
import type {
ICanvasFactory,
IContext2dFactory,
IGlobal,
IGraphicService,
ILayerHandlerContribution,
IPickerService,
IPluginService,
IRenderService,
IWindow,
IWindowHandlerContribution,
LayerMode
} from './interface';
import type { IDrawContribution } from './interface/render';

export class Application {
global: IGlobal;
graphicUtil: IGraphicUtil;
graphicService: IGraphicService;
renderService: IRenderService;
renderServiceFactory?: () => IRenderService;
pluginService?: IPluginService;
pluginServiceFactory?: () => IPluginService;
pickerServiceFactory?: () => IPickerService;
windowFactory?: () => IWindow;
windowHandlerFactory?: (env: string) => IWindowHandlerContribution;
layerHandlerFactory?: (layerMode: LayerMode) => ILayerHandlerContribution;
incrementalDrawContributionFactory?: () => IDrawContribution;
canvasFactory?: (env: string) => ICanvasFactory | undefined;
context2dFactory?: (env: string) => IContext2dFactory | undefined;
transformUtil: ITransformUtil;
layerService: ILayerService;
}

export const APPLICATION_STATE_SYMBOL = Symbol.for('@visactor/vrender-core/application-state');

export interface IApplicationState {
application: Application;
}

function createApplicationState(): IApplicationState {
return {
application: new Application()
};
}

export function getApplicationState(): IApplicationState {
const scope = globalThis as typeof globalThis & { [APPLICATION_STATE_SYMBOL]?: IApplicationState };

if (!scope[APPLICATION_STATE_SYMBOL]) {
scope[APPLICATION_STATE_SYMBOL] = createApplicationState();
}

return scope[APPLICATION_STATE_SYMBOL] as IApplicationState;
}

export const application = getApplicationState().application;
38 changes: 2 additions & 36 deletions packages/vrender-core/src/application.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,2 @@
import type { IGraphicUtil, ILayerService, ITransformUtil } from './interface/core';
import type {
ICanvasFactory,
IContext2dFactory,
IGlobal,
IGraphicService,
ILayerHandlerContribution,
IPickerService,
IPluginService,
IRenderService,
IWindow,
IWindowHandlerContribution,
LayerMode
} from './interface';
import type { IDrawContribution } from './interface/render';

export class Application {
global: IGlobal;
graphicUtil: IGraphicUtil;
graphicService: IGraphicService;
renderService: IRenderService;
renderServiceFactory?: () => IRenderService;
pluginService?: IPluginService;
pluginServiceFactory?: () => IPluginService;
pickerServiceFactory?: () => IPickerService;
windowFactory?: () => IWindow;
windowHandlerFactory?: (env: string) => IWindowHandlerContribution;
layerHandlerFactory?: (layerMode: LayerMode) => ILayerHandlerContribution;
incrementalDrawContributionFactory?: () => IDrawContribution;
canvasFactory?: (env: string) => ICanvasFactory | undefined;
context2dFactory?: (env: string) => IContext2dFactory | undefined;
transformUtil: ITransformUtil;
layerService: ILayerService;
}

export const application = new Application();
export { application, Application, APPLICATION_STATE_SYMBOL, getApplicationState } from './application-state';
export type { IApplicationState } from './application-state';
6 changes: 5 additions & 1 deletion packages/vrender-core/src/common/contribution-provider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { IBindingResolver, ServiceIdentifier } from './explicit-binding';
import type { IContributionProvider } from '../interface';
import { getContributionStoreState } from './contribution-store-state';

export const ContributionProvider = Symbol('ContributionProvider');

Expand Down Expand Up @@ -63,7 +64,7 @@ export function bindContributionProviderNoSingletonScope(bind: any, id: ServiceI
}

export class ContributionStore {
static store: Map<ServiceIdentifier<any>, Set<ContributionProviderCache<any>>> = new Map();
static store = getContributionStoreState().store as Map<ServiceIdentifier<any>, Set<ContributionProviderCache<any>>>;

static getStore(id: ServiceIdentifier<any>): ContributionProviderCache<any> | undefined {
return this.store.get(id)?.values().next().value;
Expand All @@ -86,3 +87,6 @@ export class ContributionStore {
});
}
}

export { CONTRIBUTION_STORE_STATE_SYMBOL, getContributionStoreState } from './contribution-store-state';
export type { IContributionStoreState } from './contribution-store-state';
23 changes: 23 additions & 0 deletions packages/vrender-core/src/common/contribution-store-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { ServiceIdentifier } from './explicit-binding';

export const CONTRIBUTION_STORE_STATE_SYMBOL = Symbol.for('@visactor/vrender-core/contribution-store-state');

export interface IContributionStoreState<T = any> {
store: Map<ServiceIdentifier<T>, Set<T>>;
}

function createContributionStoreState(): IContributionStoreState {
return {
store: new Map()
};
}

export function getContributionStoreState(): IContributionStoreState {
const scope = globalThis as typeof globalThis & { [CONTRIBUTION_STORE_STATE_SYMBOL]?: IContributionStoreState };

if (!scope[CONTRIBUTION_STORE_STATE_SYMBOL]) {
scope[CONTRIBUTION_STORE_STATE_SYMBOL] = createContributionStoreState();
}

return scope[CONTRIBUTION_STORE_STATE_SYMBOL] as IContributionStoreState;
}
33 changes: 33 additions & 0 deletions packages/vrender-core/src/entries/runtime-installer-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { IGlobal, IDrawItemInterceptorContribution } from '../interface';
import { createLegacyBindingContext, type ILegacyBindingContext } from '../legacy/binding-context';

export const RUNTIME_INSTALLER_STATE_SYMBOL = Symbol.for('@visactor/vrender-core/runtime-installer-state');

export interface IRuntimeInstallerState {
runtimeInstallerContext: ILegacyBindingContext;
preloaded: boolean;
runtimeGlobal?: IGlobal;
runtimeEntryKeys: WeakMap<object, Map<string, Set<string>>>;
runtimeDrawContributions: WeakMap<object, Set<IDrawItemInterceptorContribution>>;
loadedRuntimeContributionModules: WeakMap<object, WeakSet<object>>;
}

function createRuntimeInstallerState(): IRuntimeInstallerState {
return {
runtimeInstallerContext: createLegacyBindingContext(),
preloaded: false,
runtimeEntryKeys: new WeakMap<object, Map<string, Set<string>>>(),
runtimeDrawContributions: new WeakMap<object, Set<IDrawItemInterceptorContribution>>(),
loadedRuntimeContributionModules: new WeakMap<object, WeakSet<object>>()
};
}

export function getRuntimeInstallerState(): IRuntimeInstallerState {
const scope = globalThis as typeof globalThis & { [RUNTIME_INSTALLER_STATE_SYMBOL]?: IRuntimeInstallerState };

if (!scope[RUNTIME_INSTALLER_STATE_SYMBOL]) {
scope[RUNTIME_INSTALLER_STATE_SYMBOL] = createRuntimeInstallerState();
}

return scope[RUNTIME_INSTALLER_STATE_SYMBOL] as IRuntimeInstallerState;
}
Loading
Loading