mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
chore: merge blocksuite source code (#9213)
This commit is contained in:
310
blocksuite/framework/store/src/adapter/base.ts
Normal file
310
blocksuite/framework/store/src/adapter/base.ts
Normal file
@@ -0,0 +1,310 @@
|
||||
import { BlockSuiteError } from '@blocksuite/global/exceptions';
|
||||
|
||||
import type { Doc } from '../store/index.js';
|
||||
import type { AssetsManager } from '../transformer/assets.js';
|
||||
import type { DraftModel, Job, Slice } from '../transformer/index.js';
|
||||
import type {
|
||||
BlockSnapshot,
|
||||
DocSnapshot,
|
||||
SliceSnapshot,
|
||||
} from '../transformer/type.js';
|
||||
import { ASTWalkerContext } from './context.js';
|
||||
|
||||
export type FromDocSnapshotPayload = {
|
||||
snapshot: DocSnapshot;
|
||||
assets?: AssetsManager;
|
||||
};
|
||||
export type FromBlockSnapshotPayload = {
|
||||
snapshot: BlockSnapshot;
|
||||
assets?: AssetsManager;
|
||||
};
|
||||
export type FromSliceSnapshotPayload = {
|
||||
snapshot: SliceSnapshot;
|
||||
assets?: AssetsManager;
|
||||
};
|
||||
export type FromDocSnapshotResult<Target> = {
|
||||
file: Target;
|
||||
assetsIds: string[];
|
||||
};
|
||||
export type FromBlockSnapshotResult<Target> = {
|
||||
file: Target;
|
||||
assetsIds: string[];
|
||||
};
|
||||
export type FromSliceSnapshotResult<Target> = {
|
||||
file: Target;
|
||||
assetsIds: string[];
|
||||
};
|
||||
export type ToDocSnapshotPayload<Target> = {
|
||||
file: Target;
|
||||
assets?: AssetsManager;
|
||||
};
|
||||
export type ToBlockSnapshotPayload<Target> = {
|
||||
file: Target;
|
||||
assets?: AssetsManager;
|
||||
};
|
||||
export type ToSliceSnapshotPayload<Target> = {
|
||||
file: Target;
|
||||
assets?: AssetsManager;
|
||||
};
|
||||
|
||||
export function wrapFakeNote(snapshot: SliceSnapshot) {
|
||||
if (snapshot.content[0]?.flavour !== 'affine:note') {
|
||||
snapshot.content = [
|
||||
{
|
||||
type: 'block',
|
||||
id: '',
|
||||
flavour: 'affine:note',
|
||||
props: {},
|
||||
children: snapshot.content,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class BaseAdapter<AdapterTarget = unknown> {
|
||||
job: Job;
|
||||
|
||||
get configs() {
|
||||
return this.job.adapterConfigs;
|
||||
}
|
||||
|
||||
constructor(job: Job) {
|
||||
this.job = job;
|
||||
}
|
||||
|
||||
async fromBlock(model: DraftModel) {
|
||||
try {
|
||||
const blockSnapshot = this.job.blockToSnapshot(model);
|
||||
if (!blockSnapshot) return;
|
||||
return await this.fromBlockSnapshot({
|
||||
snapshot: blockSnapshot,
|
||||
assets: this.job.assetsManager,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Cannot convert block to snapshot');
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
abstract fromBlockSnapshot(
|
||||
payload: FromBlockSnapshotPayload
|
||||
):
|
||||
| Promise<FromBlockSnapshotResult<AdapterTarget>>
|
||||
| FromBlockSnapshotResult<AdapterTarget>;
|
||||
|
||||
async fromDoc(doc: Doc) {
|
||||
try {
|
||||
const docSnapshot = this.job.docToSnapshot(doc);
|
||||
if (!docSnapshot) return;
|
||||
return await this.fromDocSnapshot({
|
||||
snapshot: docSnapshot,
|
||||
assets: this.job.assetsManager,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Cannot convert doc to snapshot');
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
abstract fromDocSnapshot(
|
||||
payload: FromDocSnapshotPayload
|
||||
):
|
||||
| Promise<FromDocSnapshotResult<AdapterTarget>>
|
||||
| FromDocSnapshotResult<AdapterTarget>;
|
||||
|
||||
async fromSlice(slice: Slice) {
|
||||
try {
|
||||
const sliceSnapshot = this.job.sliceToSnapshot(slice);
|
||||
if (!sliceSnapshot) return;
|
||||
wrapFakeNote(sliceSnapshot);
|
||||
return await this.fromSliceSnapshot({
|
||||
snapshot: sliceSnapshot,
|
||||
assets: this.job.assetsManager,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Cannot convert slice to snapshot');
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
abstract fromSliceSnapshot(
|
||||
payload: FromSliceSnapshotPayload
|
||||
):
|
||||
| Promise<FromSliceSnapshotResult<AdapterTarget>>
|
||||
| FromSliceSnapshotResult<AdapterTarget>;
|
||||
|
||||
async toBlock(
|
||||
payload: ToBlockSnapshotPayload<AdapterTarget>,
|
||||
doc: Doc,
|
||||
parent?: string,
|
||||
index?: number
|
||||
) {
|
||||
try {
|
||||
const snapshot = await this.toBlockSnapshot(payload);
|
||||
if (!snapshot) return;
|
||||
return await this.job.snapshotToBlock(snapshot, doc, parent, index);
|
||||
} catch (error) {
|
||||
console.error('Cannot convert block snapshot to block');
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
abstract toBlockSnapshot(
|
||||
payload: ToBlockSnapshotPayload<AdapterTarget>
|
||||
): Promise<BlockSnapshot> | BlockSnapshot;
|
||||
|
||||
async toDoc(payload: ToDocSnapshotPayload<AdapterTarget>) {
|
||||
try {
|
||||
const snapshot = await this.toDocSnapshot(payload);
|
||||
if (!snapshot) return;
|
||||
return await this.job.snapshotToDoc(snapshot);
|
||||
} catch (error) {
|
||||
console.error('Cannot convert doc snapshot to doc');
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
abstract toDocSnapshot(
|
||||
payload: ToDocSnapshotPayload<AdapterTarget>
|
||||
): Promise<DocSnapshot> | DocSnapshot;
|
||||
|
||||
async toSlice(
|
||||
payload: ToSliceSnapshotPayload<AdapterTarget>,
|
||||
doc: Doc,
|
||||
parent?: string,
|
||||
index?: number
|
||||
) {
|
||||
try {
|
||||
const snapshot = await this.toSliceSnapshot(payload);
|
||||
if (!snapshot) return;
|
||||
return await this.job.snapshotToSlice(snapshot, doc, parent, index);
|
||||
} catch (error) {
|
||||
console.error('Cannot convert slice snapshot to slice');
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
abstract toSliceSnapshot(
|
||||
payload: ToSliceSnapshotPayload<AdapterTarget>
|
||||
): Promise<SliceSnapshot | null> | SliceSnapshot | null;
|
||||
}
|
||||
|
||||
type Keyof<T> = T extends unknown ? keyof T : never;
|
||||
|
||||
type WalkerFn<ONode extends object, TNode extends object> = (
|
||||
o: NodeProps<ONode>,
|
||||
context: ASTWalkerContext<TNode>
|
||||
) => Promise<void> | void;
|
||||
|
||||
export type NodeProps<Node extends object> = {
|
||||
node: Node;
|
||||
next?: Node | null;
|
||||
parent: NodeProps<Node> | null;
|
||||
prop: Keyof<Node> | null;
|
||||
index: number | null;
|
||||
};
|
||||
|
||||
// Ported from https://github.com/Rich-Harris/estree-walker MIT License
|
||||
export class ASTWalker<ONode extends object, TNode extends object | never> {
|
||||
private _enter: WalkerFn<ONode, TNode> | undefined;
|
||||
|
||||
private _isONode!: (node: unknown) => node is ONode;
|
||||
|
||||
private _leave: WalkerFn<ONode, TNode> | undefined;
|
||||
|
||||
private _visit = async (o: NodeProps<ONode>) => {
|
||||
if (!o.node) return;
|
||||
this.context._skipChildrenNum = 0;
|
||||
this.context._skip = false;
|
||||
|
||||
if (this._enter) {
|
||||
await this._enter(o, this.context);
|
||||
}
|
||||
|
||||
if (this.context._skip) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const key in o.node) {
|
||||
const value = o.node[key];
|
||||
|
||||
if (value && typeof value === 'object') {
|
||||
if (Array.isArray(value)) {
|
||||
for (
|
||||
let i = this.context._skipChildrenNum;
|
||||
i < value.length;
|
||||
i += 1
|
||||
) {
|
||||
const item = value[i];
|
||||
if (
|
||||
item !== null &&
|
||||
typeof item === 'object' &&
|
||||
this._isONode(item)
|
||||
) {
|
||||
const nextItem = value[i + 1] ?? null;
|
||||
await this._visit({
|
||||
node: item,
|
||||
next: nextItem,
|
||||
parent: o,
|
||||
prop: key as unknown as Keyof<ONode>,
|
||||
index: i,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
this.context._skipChildrenNum === 0 &&
|
||||
this._isONode(value)
|
||||
) {
|
||||
await this._visit({
|
||||
node: value,
|
||||
next: null,
|
||||
parent: o,
|
||||
prop: key as unknown as Keyof<ONode>,
|
||||
index: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this._leave) {
|
||||
await this._leave(o, this.context);
|
||||
}
|
||||
};
|
||||
|
||||
private context: ASTWalkerContext<TNode>;
|
||||
|
||||
setEnter = (fn: WalkerFn<ONode, TNode>) => {
|
||||
this._enter = fn;
|
||||
};
|
||||
|
||||
setLeave = (fn: WalkerFn<ONode, TNode>) => {
|
||||
this._leave = fn;
|
||||
};
|
||||
|
||||
setONodeTypeGuard = (fn: (node: unknown) => node is ONode) => {
|
||||
this._isONode = fn;
|
||||
};
|
||||
|
||||
walk = async (oNode: ONode, tNode: TNode) => {
|
||||
this.context.openNode(tNode);
|
||||
await this._visit({ node: oNode, parent: null, prop: null, index: null });
|
||||
if (this.context.stack.length !== 1) {
|
||||
throw new BlockSuiteError(1, 'There are unclosed nodes');
|
||||
}
|
||||
return this.context.currentNode();
|
||||
};
|
||||
|
||||
walkONode = async (oNode: ONode) => {
|
||||
await this._visit({ node: oNode, parent: null, prop: null, index: null });
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this.context = new ASTWalkerContext<TNode>();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user