mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-23 21:38:44 +08:00
refactor(editor): remove blocks package (#10708)
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,109 @@
|
||||
import { DefaultTheme } from '@blocksuite/affine-model';
|
||||
import { NotionTextAdapter } from '@blocksuite/affine-shared/adapters';
|
||||
import type { SliceSnapshot } from '@blocksuite/store';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import { createJob } from '../utils/create-job.js';
|
||||
import { getProvider } from '../utils/get-provider.js';
|
||||
import { nanoidReplacement } from '../utils/nanoid-replacement.js';
|
||||
|
||||
getProvider();
|
||||
|
||||
describe('notion-text to snapshot', () => {
|
||||
test('basic', () => {
|
||||
const notionText =
|
||||
'{"blockType":"text","editing":[["aaa ",[["_"],["b"],["i"]]],["nbbbb ",[["_"],["i"]]],["hjhj ",[["_"]]],["a",[["_"],["c"]]],[" ",[["_"]]],["ccc d",[["_"],["s"]]],["dd",[["_"],["s"]]]]}';
|
||||
|
||||
const sliceSnapshot: SliceSnapshot = {
|
||||
type: 'slice',
|
||||
content: [
|
||||
{
|
||||
type: 'block',
|
||||
id: 'matchesReplaceMap[0]',
|
||||
flavour: 'affine:note',
|
||||
props: {
|
||||
xywh: '[0,0,800,95]',
|
||||
background: DefaultTheme.noteBackgrounColor,
|
||||
index: 'a0',
|
||||
hidden: false,
|
||||
displayMode: 'both',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
type: 'block',
|
||||
id: 'matchesReplaceMap[1]',
|
||||
flavour: 'affine:paragraph',
|
||||
props: {
|
||||
type: 'text',
|
||||
text: {
|
||||
'$blocksuite:internal:text$': true,
|
||||
delta: [
|
||||
{
|
||||
insert: 'aaa ',
|
||||
attributes: {
|
||||
underline: true,
|
||||
bold: true,
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: 'nbbbb ',
|
||||
attributes: {
|
||||
underline: true,
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: 'hjhj ',
|
||||
attributes: {
|
||||
underline: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: 'a',
|
||||
attributes: {
|
||||
underline: true,
|
||||
code: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: ' ',
|
||||
attributes: {
|
||||
underline: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: 'ccc d',
|
||||
attributes: {
|
||||
underline: true,
|
||||
strike: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: 'dd',
|
||||
attributes: {
|
||||
underline: true,
|
||||
strike: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
workspaceId: '',
|
||||
pageId: '',
|
||||
};
|
||||
|
||||
const ntAdapter = new NotionTextAdapter(createJob());
|
||||
const target = ntAdapter.toSliceSnapshot({
|
||||
file: notionText,
|
||||
workspaceId: '',
|
||||
pageId: '',
|
||||
});
|
||||
expect(nanoidReplacement(target!)).toEqual(sliceSnapshot);
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,236 @@
|
||||
import {
|
||||
addProperty,
|
||||
copyCellsByProperty,
|
||||
databaseBlockColumns,
|
||||
deleteColumn,
|
||||
getCell,
|
||||
getProperty,
|
||||
updateCell,
|
||||
} from '@blocksuite/affine-block-database';
|
||||
import {
|
||||
type Cell,
|
||||
type Column,
|
||||
type DatabaseBlockModel,
|
||||
DatabaseBlockSchemaExtension,
|
||||
NoteBlockSchemaExtension,
|
||||
ParagraphBlockSchemaExtension,
|
||||
RootBlockSchemaExtension,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { propertyModelPresets } from '@blocksuite/data-view/property-pure-presets';
|
||||
import type { BlockModel, Store } from '@blocksuite/store';
|
||||
import { Text } from '@blocksuite/store';
|
||||
import {
|
||||
createAutoIncrementIdGenerator,
|
||||
TestWorkspace,
|
||||
} from '@blocksuite/store/test';
|
||||
import { beforeEach, describe, expect, test } from 'vitest';
|
||||
|
||||
const extensions = [
|
||||
RootBlockSchemaExtension,
|
||||
NoteBlockSchemaExtension,
|
||||
ParagraphBlockSchemaExtension,
|
||||
DatabaseBlockSchemaExtension,
|
||||
];
|
||||
|
||||
function createTestOptions() {
|
||||
const idGenerator = createAutoIncrementIdGenerator();
|
||||
return { id: 'test-collection', idGenerator };
|
||||
}
|
||||
|
||||
function createTestDoc(docId = 'doc0') {
|
||||
const options = createTestOptions();
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc({ id: docId, extensions });
|
||||
doc.load();
|
||||
return doc;
|
||||
}
|
||||
|
||||
describe('DatabaseManager', () => {
|
||||
let doc: Store;
|
||||
let db: DatabaseBlockModel;
|
||||
|
||||
let rootId: BlockModel['id'];
|
||||
let noteBlockId: BlockModel['id'];
|
||||
let databaseBlockId: BlockModel['id'];
|
||||
let p1: BlockModel['id'];
|
||||
let p2: BlockModel['id'];
|
||||
let col1: Column['id'];
|
||||
let col2: Column['id'];
|
||||
let col3: Column['id'];
|
||||
|
||||
const selection = [
|
||||
{ id: '1', value: 'Done', color: 'var(--affine-tag-white)' },
|
||||
{ id: '2', value: 'TODO', color: 'var(--affine-tag-pink)' },
|
||||
{ id: '3', value: 'WIP', color: 'var(--affine-tag-blue)' },
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
doc = createTestDoc();
|
||||
|
||||
rootId = doc.addBlock('affine:page', {
|
||||
title: new Text('database test'),
|
||||
});
|
||||
noteBlockId = doc.addBlock('affine:note', {}, rootId);
|
||||
|
||||
databaseBlockId = doc.addBlock(
|
||||
'affine:database',
|
||||
{
|
||||
columns: [],
|
||||
titleColumn: 'Title',
|
||||
},
|
||||
noteBlockId
|
||||
);
|
||||
|
||||
const databaseModel = doc.getBlockById(
|
||||
databaseBlockId
|
||||
) as DatabaseBlockModel;
|
||||
db = databaseModel;
|
||||
|
||||
col1 = addProperty(
|
||||
db,
|
||||
'end',
|
||||
databaseBlockColumns.numberColumnConfig.create('Number')
|
||||
);
|
||||
col2 = addProperty(
|
||||
db,
|
||||
'end',
|
||||
propertyModelPresets.selectPropertyModelConfig.create('Single Select', {
|
||||
options: selection,
|
||||
})
|
||||
);
|
||||
col3 = addProperty(
|
||||
db,
|
||||
'end',
|
||||
databaseBlockColumns.richTextColumnConfig.create('Rich Text')
|
||||
);
|
||||
|
||||
doc.updateBlock(databaseModel, {
|
||||
columns: [col1, col2, col3],
|
||||
});
|
||||
|
||||
p1 = doc.addBlock(
|
||||
'affine:paragraph',
|
||||
{
|
||||
text: new Text('text1'),
|
||||
},
|
||||
databaseBlockId
|
||||
);
|
||||
p2 = doc.addBlock(
|
||||
'affine:paragraph',
|
||||
{
|
||||
text: new Text('text2'),
|
||||
},
|
||||
databaseBlockId
|
||||
);
|
||||
|
||||
updateCell(db, p1, {
|
||||
columnId: col1,
|
||||
value: 0.1,
|
||||
});
|
||||
updateCell(db, p2, {
|
||||
columnId: col2,
|
||||
value: [selection[1]],
|
||||
});
|
||||
});
|
||||
|
||||
test('getColumn', () => {
|
||||
const column = {
|
||||
...databaseBlockColumns.numberColumnConfig.create('testColumnId'),
|
||||
id: 'testColumnId',
|
||||
};
|
||||
addProperty(db, 'end', column);
|
||||
|
||||
const result = getProperty(db, column.id);
|
||||
expect(result).toEqual(column);
|
||||
});
|
||||
|
||||
test('addColumn', () => {
|
||||
const column =
|
||||
databaseBlockColumns.numberColumnConfig.create('Test Column');
|
||||
const id = addProperty(db, 'end', column);
|
||||
const result = getProperty(db, id);
|
||||
|
||||
expect(result).toMatchObject(column);
|
||||
expect(result).toHaveProperty('id');
|
||||
});
|
||||
|
||||
test('deleteColumn', () => {
|
||||
const column = {
|
||||
...databaseBlockColumns.numberColumnConfig.create('Test Column'),
|
||||
id: 'testColumnId',
|
||||
};
|
||||
addProperty(db, 'end', column);
|
||||
expect(getProperty(db, column.id)).toEqual(column);
|
||||
|
||||
deleteColumn(db, column.id);
|
||||
expect(getProperty(db, column.id)).toBeUndefined();
|
||||
});
|
||||
|
||||
test('getCell', () => {
|
||||
const modelId = doc.addBlock(
|
||||
'affine:paragraph',
|
||||
{
|
||||
text: new Text('paragraph'),
|
||||
},
|
||||
noteBlockId
|
||||
);
|
||||
const column = {
|
||||
...databaseBlockColumns.numberColumnConfig.create('Test Column'),
|
||||
id: 'testColumnId',
|
||||
};
|
||||
const cell: Cell = {
|
||||
columnId: column.id,
|
||||
value: 42,
|
||||
};
|
||||
|
||||
addProperty(db, 'end', column);
|
||||
updateCell(db, modelId, cell);
|
||||
|
||||
const model = doc.getBlockById(modelId);
|
||||
|
||||
expect(model).not.toBeNull();
|
||||
|
||||
const result = getCell(db, model!.id, column.id);
|
||||
expect(result).toEqual(cell);
|
||||
});
|
||||
|
||||
test('updateCell', () => {
|
||||
const newRowId = doc.addBlock(
|
||||
'affine:paragraph',
|
||||
{
|
||||
text: new Text('text3'),
|
||||
},
|
||||
databaseBlockId
|
||||
);
|
||||
|
||||
updateCell(db, newRowId, {
|
||||
columnId: col2,
|
||||
value: [selection[2]],
|
||||
});
|
||||
|
||||
const cell = getCell(db, newRowId, col2);
|
||||
expect(cell).toEqual({
|
||||
columnId: col2,
|
||||
value: [selection[2]],
|
||||
});
|
||||
});
|
||||
|
||||
test('copyCellsByColumn', () => {
|
||||
const newColId = addProperty(
|
||||
db,
|
||||
'end',
|
||||
propertyModelPresets.selectPropertyModelConfig.create('Copied Select', {
|
||||
options: selection,
|
||||
})
|
||||
);
|
||||
|
||||
copyCellsByProperty(db, col2, newColId);
|
||||
|
||||
const cell = getCell(db, p2, newColId);
|
||||
expect(cell).toEqual({
|
||||
columnId: newColId,
|
||||
value: [selection[1]],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
import { type SelectTag, t, typeSystem } from '@blocksuite/data-view';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
describe('subtyping', () => {
|
||||
test('all type is subtype of unknown', () => {
|
||||
expect(typeSystem.unify(t.boolean.instance(), t.unknown.instance())).toBe(
|
||||
true
|
||||
);
|
||||
expect(typeSystem.unify(t.string.instance(), t.unknown.instance())).toBe(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
typeSystem.unify(
|
||||
t.array.instance(t.string.instance()),
|
||||
t.unknown.instance()
|
||||
)
|
||||
).toBe(true);
|
||||
expect(typeSystem.unify(t.tag.instance(), t.unknown.instance())).toBe(true);
|
||||
});
|
||||
});
|
||||
describe('function apply', () => {
|
||||
test('generic type function', () => {
|
||||
const fn = t.fn.instance(
|
||||
[t.typeVarReference.create('A'), t.typeVarReference.create('A')],
|
||||
t.boolean.instance(),
|
||||
[t.typeVarDefine.create('A', t.unknown.instance())]
|
||||
);
|
||||
const instancedFn = typeSystem.instanceFn(
|
||||
fn,
|
||||
[t.boolean.instance()],
|
||||
t.boolean.instance(),
|
||||
{}
|
||||
);
|
||||
expect(instancedFn?.args[1]).toStrictEqual(t.boolean.instance());
|
||||
});
|
||||
test('tags infer', () => {
|
||||
const fn = t.fn.instance(
|
||||
[
|
||||
t.typeVarReference.create('A'),
|
||||
t.array.instance(t.typeVarReference.create('A')),
|
||||
] as const,
|
||||
t.boolean.instance(),
|
||||
[t.typeVarDefine.create('A', t.tag.instance())]
|
||||
);
|
||||
const fnArray = t.fn.instance(
|
||||
[
|
||||
t.array.instance(t.typeVarReference.create('A')),
|
||||
t.array.instance(t.typeVarReference.create('A')),
|
||||
] as const,
|
||||
t.boolean.instance(),
|
||||
[t.typeVarDefine.create('A', t.tag.instance())]
|
||||
);
|
||||
const tags: SelectTag[] = [{ id: 'a', value: 'b', color: 'c' }];
|
||||
const instancedFn = typeSystem.instanceFn(
|
||||
fn,
|
||||
[t.tag.instance(tags)],
|
||||
t.boolean.instance(),
|
||||
{}
|
||||
);
|
||||
const instancedFnArray = typeSystem.instanceFn(
|
||||
fnArray,
|
||||
[t.array.instance(t.tag.instance(tags))],
|
||||
t.boolean.instance(),
|
||||
{}
|
||||
);
|
||||
expect(
|
||||
typeSystem.unify(
|
||||
instancedFn?.args[1],
|
||||
t.array.instance(t.tag.instance(tags))
|
||||
)
|
||||
).toBe(true);
|
||||
expect(
|
||||
typeSystem.unify(
|
||||
instancedFnArray?.args[1],
|
||||
t.array.instance(t.tag.instance(tags))
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import { defaultImageProxyMiddleware } from '@blocksuite/affine-block-image';
|
||||
import { SpecProvider } from '@blocksuite/affine-shared/utils';
|
||||
import {
|
||||
Schema,
|
||||
Transformer,
|
||||
type TransformerMiddleware,
|
||||
} from '@blocksuite/store';
|
||||
import { TestWorkspace } from '@blocksuite/store/test';
|
||||
|
||||
import { AffineSchemas } from '../../schemas.js';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
happyDOM: {
|
||||
settings: {
|
||||
fetch: {
|
||||
disableSameOriginPolicy: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function createJob(middlewares?: TransformerMiddleware[]) {
|
||||
window.happyDOM.settings.fetch.disableSameOriginPolicy = true;
|
||||
const testMiddlewares = middlewares ?? [];
|
||||
testMiddlewares.push(defaultImageProxyMiddleware);
|
||||
const schema = new Schema().register(AffineSchemas);
|
||||
const docCollection = new TestWorkspace();
|
||||
docCollection.storeExtensions = SpecProvider._.getSpec('store').value;
|
||||
docCollection.meta.initialize();
|
||||
return new Transformer({
|
||||
schema,
|
||||
blobCRUD: docCollection.blobSync,
|
||||
middlewares: testMiddlewares,
|
||||
docCRUD: {
|
||||
create: (id: string) => docCollection.createDoc({ id }),
|
||||
get: (id: string) => docCollection.getDoc(id),
|
||||
delete: (id: string) => docCollection.removeDoc(id),
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { SpecProvider } from '@blocksuite/affine-shared/utils';
|
||||
import { Container } from '@blocksuite/global/di';
|
||||
|
||||
import { registerSpecs } from '../../extensions/register';
|
||||
|
||||
registerSpecs();
|
||||
|
||||
export function getProvider() {
|
||||
const container = new Container();
|
||||
const exts = SpecProvider._.getSpec('store').value;
|
||||
exts.forEach(ext => {
|
||||
ext.setup(container);
|
||||
});
|
||||
return container.provider();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { BlockSnapshot, SliceSnapshot } from '@blocksuite/store';
|
||||
|
||||
export function nanoidReplacement(snapshot: BlockSnapshot | SliceSnapshot) {
|
||||
return JSON.parse(nanoidReplacementString(JSON.stringify(snapshot)));
|
||||
}
|
||||
const escapedSnapshotAttributes = new Set([
|
||||
'"attributes"',
|
||||
'"conditions"',
|
||||
'"iconColumn"',
|
||||
'"background"',
|
||||
'"LinkedPage"',
|
||||
'"elementIds"',
|
||||
'"attachment"',
|
||||
]);
|
||||
|
||||
function nanoidReplacementString(snapshotString: string) {
|
||||
const re =
|
||||
/("block:[A-Za-z0-9-_]{10}")|("[A-Za-z0-9-_]{10}")|("[A-Za-z0-9-_]{35}")|("[A-Za-z0-9-_]{10}:[A-Za-z0-9-_]{10}")|("var\(--affine-v2-chip-label-[a-z]{3,10}\)")|("[A-Za-z0-9-_=]{44}")/g;
|
||||
const matches = snapshotString.matchAll(re);
|
||||
const matchesReplaceMap = new Map();
|
||||
let escapedNumber = 0;
|
||||
Array.from(matches).forEach((match, index) => {
|
||||
if (escapedSnapshotAttributes.has(match[0])) {
|
||||
matchesReplaceMap.set(match[0], match[0]);
|
||||
escapedNumber++;
|
||||
} else {
|
||||
matchesReplaceMap.set(
|
||||
match[0],
|
||||
`"matchesReplaceMap[${index - escapedNumber}]"`
|
||||
);
|
||||
}
|
||||
});
|
||||
return snapshotString.replace(re, match => matchesReplaceMap.get(match));
|
||||
}
|
||||
Reference in New Issue
Block a user