refactor(editor): remove assert functions (#10629)

This commit is contained in:
Saul-Mirone
2025-03-05 10:20:02 +00:00
parent 201c3438ba
commit 7e39893aac
17 changed files with 63 additions and 95 deletions

View File

@@ -1,6 +1,5 @@
import type { LatexProps } from '@blocksuite/affine-model'; import type { LatexProps } from '@blocksuite/affine-model';
import type { Command } from '@blocksuite/block-std'; import type { Command } from '@blocksuite/block-std';
import { assertInstanceOf } from '@blocksuite/global/utils';
import type { BlockModel } from '@blocksuite/store'; import type { BlockModel } from '@blocksuite/store';
import { LatexBlockComponent } from './latex-block.js'; import { LatexBlockComponent } from './latex-block.js';
@@ -46,9 +45,10 @@ export const insertLatexBlockCommand: Command<
insertedLatexBlockId: std.host.updateComplete.then(async () => { insertedLatexBlockId: std.host.updateComplete.then(async () => {
if (!latex) { if (!latex) {
const blockComponent = std.view.getBlock(result[0]); const blockComponent = std.view.getBlock(result[0]);
assertInstanceOf(blockComponent, LatexBlockComponent); if (blockComponent instanceof LatexBlockComponent) {
await blockComponent.updateComplete; await blockComponent.updateComplete;
blockComponent.toggleEditor(); blockComponent.toggleEditor();
}
} }
return result[0]; return result[0];
}), }),

View File

@@ -26,7 +26,10 @@ import {
FeatureFlagService, FeatureFlagService,
ThemeProvider, ThemeProvider,
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import { captureEventTarget } from '@blocksuite/affine-shared/utils'; import {
captureEventTarget,
matchModels,
} from '@blocksuite/affine-shared/utils';
import { type BlockStdScope, stdContext } from '@blocksuite/block-std'; import { type BlockStdScope, stdContext } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx'; import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import type { XYWH } from '@blocksuite/global/gfx'; import type { XYWH } from '@blocksuite/global/gfx';
@@ -38,7 +41,7 @@ import {
toDegree, toDegree,
Vec, Vec,
} from '@blocksuite/global/gfx'; } from '@blocksuite/global/gfx';
import { assertInstanceOf, WithDisposable } from '@blocksuite/global/utils'; import { WithDisposable } from '@blocksuite/global/utils';
import { FrameIcon, PageIcon } from '@blocksuite/icons/lit'; import { FrameIcon, PageIcon } from '@blocksuite/icons/lit';
import { consume } from '@lit/context'; import { consume } from '@lit/context';
import { baseTheme } from '@toeverything/theme'; import { baseTheme } from '@toeverything/theme';
@@ -192,7 +195,9 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) {
doc.root?.id doc.root?.id
); );
const note = doc.getBlock(id)?.model; const note = doc.getBlock(id)?.model;
assertInstanceOf(note, NoteBlockModel); if (!matchModels(note, [NoteBlockModel])) {
return;
}
doc.addBlock('affine:paragraph', { type: 'text' }, id); doc.addBlock('affine:paragraph', { type: 'text' }, id);
const group = this.currentSource.group; const group = this.currentSource.group;
@@ -285,7 +290,9 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) {
}); });
if (!textId) return; if (!textId) return;
const textElement = this.crud.getElementById(textId); const textElement = this.crud.getElementById(textId);
assertInstanceOf(textElement, TextElementModel); if (!(textElement instanceof TextElementModel)) {
return;
}
this.crud.updateElement(this.connector.id, { this.crud.updateElement(this.connector.id, {
target: { id: textId, position }, target: { id: textId, position },

View File

@@ -13,7 +13,6 @@ import {
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import { openFileOrFiles } from '@blocksuite/affine-shared/utils'; import { openFileOrFiles } from '@blocksuite/affine-shared/utils';
import { Bound } from '@blocksuite/global/gfx'; import { Bound } from '@blocksuite/global/gfx';
import { assertInstanceOf } from '@blocksuite/global/utils';
import type { TemplateResult } from 'lit'; import type { TemplateResult } from 'lit';
import * as Y from 'yjs'; import * as Y from 'yjs';
@@ -143,7 +142,10 @@ export const textRender: DraggableTool['render'] = async (
edgeless.doc.captureSync(); edgeless.doc.captureSync();
const textElement = edgeless.service.crud.getElementById(id); const textElement = edgeless.service.crud.getElementById(id);
assertInstanceOf(textElement, TextElementModel); if (!(textElement instanceof TextElementModel)) {
console.error('Cannot mount text editor on a non-text element');
return null;
}
mountTextElementEditor(textElement, edgeless); mountTextElementEditor(textElement, edgeless);
} }

View File

@@ -14,7 +14,6 @@ import type { PointerEventState } from '@blocksuite/block-std';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions'; import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import type { IVec } from '@blocksuite/global/gfx'; import type { IVec } from '@blocksuite/global/gfx';
import { Bound } from '@blocksuite/global/gfx'; import { Bound } from '@blocksuite/global/gfx';
import { assertInstanceOf } from '@blocksuite/global/utils';
import * as Y from 'yjs'; import * as Y from 'yjs';
import { EdgelessConnectorLabelEditor } from '../components/text/edgeless-connector-label-editor.js'; import { EdgelessConnectorLabelEditor } from '../components/text/edgeless-connector-label-editor.js';
@@ -82,11 +81,10 @@ export function mountShapeTextEditor(
const updatedElement = edgeless.service.crud.getElementById(shapeElement.id); const updatedElement = edgeless.service.crud.getElementById(shapeElement.id);
assertInstanceOf( if (!(updatedElement instanceof ShapeElementModel)) {
updatedElement, console.error('Cannot mount text editor on a non-shape element');
ShapeElementModel, return;
'Cannot mount text editor on a non-shape element' }
);
const shapeEditor = new EdgelessShapeTextEditor(); const shapeEditor = new EdgelessShapeTextEditor();
shapeEditor.element = updatedElement; shapeEditor.element = updatedElement;

View File

@@ -11,7 +11,6 @@ import {
type PointerEventState, type PointerEventState,
WidgetComponent, WidgetComponent,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import { assertInstanceOf } from '@blocksuite/global/utils';
import { html, nothing } from 'lit'; import { html, nothing } from 'lit';
import { state } from 'lit/decorators.js'; import { state } from 'lit/decorators.js';
import { styleMap } from 'lit/directives/style-map.js'; import { styleMap } from 'lit/directives/style-map.js';
@@ -443,9 +442,14 @@ function getSelectingBlockPaths(blockInfos: BlockInfo[], userRect: Rect) {
function isDragArea(e: PointerEventState) { function isDragArea(e: PointerEventState) {
const el = e.raw.target; const el = e.raw.target;
assertInstanceOf(el, Element); if (!(el instanceof Element)) {
return false;
}
const block = el.closest<BlockComponent>(`[${BLOCK_ID_ATTR}]`); const block = el.closest<BlockComponent>(`[${BLOCK_ID_ATTR}]`);
return block && matchModels(block.model, [RootBlockModel, NoteBlockModel]); if (!block) {
return false;
}
return matchModels(block.model, [RootBlockModel, NoteBlockModel]);
} }
declare global { declare global {

View File

@@ -32,7 +32,7 @@ import {
toRadian, toRadian,
Vec, Vec,
} from '@blocksuite/global/gfx'; } from '@blocksuite/global/gfx';
import { assertEquals, assertType, last } from '@blocksuite/global/utils'; import { assertType, last } from '@blocksuite/global/utils';
import { effect } from '@preact/signals-core'; import { effect } from '@preact/signals-core';
import { Overlay } from '../renderer/overlay.js'; import { Overlay } from '../renderer/overlay.js';
@@ -606,13 +606,10 @@ function mergePath(points: IVec[] | IVec3[]) {
for (let i = 0; i < result.length - 1; i++) { for (let i = 0; i < result.length - 1; i++) {
const cur = result[i]; const cur = result[i];
const next = result[i + 1]; const next = result[i + 1];
try { const isAlmostEqual =
assertEquals( almostEqual(cur[0], next[0], 0.02) || almostEqual(cur[1], next[1], 0.02);
almostEqual(cur[0], next[0], 0.02) || if (!isAlmostEqual) {
almostEqual(cur[1], next[1], 0.02), console.warn('Expected equal points');
true
);
} catch {
console.warn(points); console.warn(points);
console.warn(result); console.warn(result);
} }

View File

@@ -1,5 +1,5 @@
import { ShadowlessElement } from '@blocksuite/block-std'; import { ShadowlessElement } from '@blocksuite/block-std';
import { assertEquals } from '@blocksuite/global/utils'; import { isEqual } from '@blocksuite/global/utils';
import { type Text } from '@blocksuite/store'; import { type Text } from '@blocksuite/store';
import { css, html } from 'lit'; import { css, html } from 'lit';
import { state } from 'lit/decorators.js'; import { state } from 'lit/decorators.js';
@@ -63,11 +63,10 @@ export function fillSelectionWithFocusCellData(
if (!focusCell) return; if (!focusCell) return;
if (rowsSelection && columnsSelection) { if (rowsSelection && columnsSelection) {
assertEquals( if (!isEqual(columnsSelection.start, columnsSelection.end)) {
columnsSelection.start, console.error('expected selections on a single column');
columnsSelection.end, return;
'expected selections on a single column' }
);
const curCol = focusCell.column; // we are sure that we are always in the same column while iterating through rows const curCol = focusCell.column; // we are sure that we are always in the same column while iterating through rows
const cell = focusCell.cell$.value; const cell = focusCell.cell$.value;

View File

@@ -11,10 +11,11 @@ type ModelList<T> =
export function matchModels< export function matchModels<
const Model extends ConstructorType<BlockModel>[], const Model extends ConstructorType<BlockModel>[],
U extends ModelList<Model>[number] = ModelList<Model>[number], U extends ModelList<Model>[number] = ModelList<Model>[number],
>(model: BlockModel | null, expected: Model): model is U { >(model: BlockModel | null | undefined, expected: Model): model is U {
return ( if (model === null || model === undefined) {
!!model && expected.some(expectedModel => model instanceof expectedModel) return false;
); }
return expected.some(expectedModel => model instanceof expectedModel);
} }
export function isInsideBlockByFlavour( export function isInsideBlockByFlavour(

View File

@@ -1,7 +1,7 @@
export * from './assert.js';
export * from './crypto.js'; export * from './crypto.js';
export * from './disposable.js'; export * from './disposable.js';
export * from './function.js'; export * from './function.js';
export * from './is-equal.js';
export * from './iterable.js'; export * from './iterable.js';
export * from './logger.js'; export * from './logger.js';
export * from './signal-watcher.js'; export * from './signal-watcher.js';

View File

@@ -1,7 +1,4 @@
// https://stackoverflow.com/questions/31538010/test-if-a-variable-is-a-primitive-rather-than-an-object // https://stackoverflow.com/questions/31538010/test-if-a-variable-is-a-primitive-rather-than-an-object
import { ErrorCode } from '../exceptions/code.js';
import { BlockSuiteError } from '../exceptions/index.js';
export function isPrimitive( export function isPrimitive(
a: unknown a: unknown
): a is null | undefined | boolean | number | string { ): a is null | undefined | boolean | number | string {
@@ -10,16 +7,6 @@ export function isPrimitive(
export function assertType<T>(_: unknown): asserts _ is T {} export function assertType<T>(_: unknown): asserts _ is T {}
export function assertNotExists<T>(
val: T | null | undefined,
message = 'val exists',
errorCode = ErrorCode.ValueNotExists
): asserts val is null | undefined {
if (val !== null && val !== undefined) {
throw new BlockSuiteError(errorCode, message);
}
}
export type Equals<X, Y> = export type Equals<X, Y> =
/// ///
(<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2
@@ -65,26 +52,3 @@ export function isEqual<T extends Allowed, U extends T>(
} }
return true as Equals<T, U>; return true as Equals<T, U>;
} }
export function assertEquals<T extends Allowed, U extends T>(
val: T,
expected: U,
message = 'val is not same as expected',
errorCode = ErrorCode.ValueNotEqual
): asserts val is U {
if (!isEqual(val, expected)) {
throw new BlockSuiteError(errorCode, message);
}
}
type Class<T> = new (...args: any[]) => T;
export function assertInstanceOf<T>(
val: unknown,
expected: Class<T>,
message = 'val is not instance of expected',
errorCode = ErrorCode.ValueNotInstanceOf
): asserts val is T {
if (!(val instanceof expected)) {
throw new BlockSuiteError(errorCode, message);
}
}

View File

@@ -1,4 +1,3 @@
import { assertInstanceOf } from '@blocksuite/global/utils';
import { effect } from '@preact/signals-core'; import { effect } from '@preact/signals-core';
import * as Y from 'yjs'; import * as Y from 'yjs';
@@ -47,7 +46,9 @@ export class RangeService<TextAttributes extends BaseTextAttributes> {
return null; return null;
} }
const textNode = text.childNodes[1]; const textNode = text.childNodes[1];
assertInstanceOf(textNode, Text); if (!(textNode instanceof Text)) {
return null;
}
range.setStart(textNode, 0); range.setStart(textNode, 0);
range.setEnd(textNode, textNode.textContent?.length ?? 0); range.setEnd(textNode, textNode.textContent?.length ?? 0);
const inlineRange = this.toInlineRange(range); const inlineRange = this.toInlineRange(range);

View File

@@ -33,7 +33,7 @@ export default defineConfig(_configEnv =>
coverage: { coverage: {
provider: 'istanbul', // or 'c8' provider: 'istanbul', // or 'c8'
reporter: ['lcov'], reporter: ['lcov'],
reportsDirectory: '../../.coverage/presets', reportsDirectory: '../../.coverage/integration-test',
}, },
deps: { deps: {
interopDefault: true, interopDefault: true,

View File

@@ -1,4 +1,3 @@
import { assertNotExists } from '@blocksuite/global/utils';
import { expect } from '@playwright/test'; import { expect } from '@playwright/test';
import { import {
@@ -90,7 +89,7 @@ test.describe('note to linked doc', () => {
const moreButton = locatorComponentToolbarMoreButton(page); const moreButton = locatorComponentToolbarMoreButton(page);
await moreButton.click(); await moreButton.click();
const turnButton = page.locator('.turn-into-linked-doc'); const turnButton = page.locator('.turn-into-linked-doc');
assertNotExists(turnButton); expect(turnButton).toBeNull();
}); });
// TODO FIX ME // TODO FIX ME
@@ -111,7 +110,7 @@ test.describe('note to linked doc', () => {
const moreButton = locatorComponentToolbarMoreButton(page); const moreButton = locatorComponentToolbarMoreButton(page);
await moreButton.click(); await moreButton.click();
const turnButton = page.locator('.turn-into-linked-doc'); const turnButton = page.locator('.turn-into-linked-doc');
assertNotExists(turnButton); expect(turnButton).toBeNull();
}); });
}); });

View File

@@ -188,11 +188,6 @@ export default tseslint.config(
message: "Don't import from src", message: "Don't import from src",
allowTypeImports: false, allowTypeImports: false,
}, },
{
group: ['@blocksuite/store'],
message: "Import from '@blocksuite/global/utils'",
importNames: ['assertEquals'],
},
], ],
}, },
], ],

View File

@@ -11,7 +11,6 @@ import { DebugLogger } from '@affine/debug';
import type { ListHistoryQuery } from '@affine/graphql'; import type { ListHistoryQuery } from '@affine/graphql';
import { listHistoryQuery, recoverDocMutation } from '@affine/graphql'; import { listHistoryQuery, recoverDocMutation } from '@affine/graphql';
import { i18nTime } from '@affine/i18n'; import { i18nTime } from '@affine/i18n';
import { assertEquals } from '@blocksuite/affine/global/utils';
import type { Workspace } from '@blocksuite/affine/store'; import type { Workspace } from '@blocksuite/affine/store';
import { useService } from '@toeverything/infra'; import { useService } from '@toeverything/infra';
import { useEffect, useMemo } from 'react'; import { useEffect, useMemo } from 'react';
@@ -291,7 +290,9 @@ export const useRestorePage = (docCollection: Workspace, pageId: string) => {
} }
const pageDocId = page.spaceDoc.guid; const pageDocId = page.spaceDoc.guid;
revertUpdate(page.spaceDoc, update, key => { revertUpdate(page.spaceDoc, update, key => {
assertEquals(key, 'blocks'); // only expect this value is 'blocks' if (key !== 'blocks') {
throw new Error('Only expect this value is "blocks"');
}
return 'Map'; return 'Map';
}); });

View File

@@ -1,4 +1,3 @@
import { assertEquals } from '@blocksuite/affine/global/utils';
import { Service } from '@toeverything/infra'; import { Service } from '@toeverything/infra';
import { applyUpdate } from 'yjs'; import { applyUpdate } from 'yjs';
@@ -26,7 +25,11 @@ export class WorkspaceTransformService extends Service {
accountId: string, accountId: string,
flavour: string flavour: string
): Promise<WorkspaceMetadata> => { ): Promise<WorkspaceMetadata> => {
assertEquals(local.flavour, 'local'); if (local.flavour !== 'local') {
throw new Error(
'Only local workspace can be transformed to cloud workspace'
);
}
const localDocStorage = local.engine.doc.storage; const localDocStorage = local.engine.doc.storage;
const localDocList = Array.from(local.docCollection.docs.keys()); const localDocList = Array.from(local.docCollection.docs.keys());

View File

@@ -1,13 +1,10 @@
import type { ToastOptions } from '@affine/component'; import type { ToastOptions } from '@affine/component';
import { toast as basicToast } from '@affine/component'; import { toast as basicToast } from '@affine/component';
import { assertEquals } from '@blocksuite/affine/global/utils';
export const toast = (message: string, options?: ToastOptions) => { export const toast = (message: string, options?: ToastOptions) => {
const modal = document.querySelector( const modal = document.querySelector<HTMLDivElement>('[role=presentation]');
'[role=presentation]' if (modal && !(modal instanceof HTMLDivElement)) {
) as HTMLDivElement | null; throw new Error('modal should be div');
if (modal) {
assertEquals(modal.constructor, HTMLDivElement, 'modal should be div');
} }
return basicToast(message, { return basicToast(message, {
portal: modal || document.body, portal: modal || document.body,