feat: add new rule for floating promise (#2726)

Co-authored-by: Himself65 <himself65@outlook.com>
(cherry picked from commit bedf838fe5)
This commit is contained in:
Peng Xiao
2023-06-08 17:54:25 +08:00
committed by Alex Yang
parent 47babe25b7
commit 5699c99bf6
45 changed files with 221 additions and 134 deletions
@@ -26,9 +26,13 @@ function rpcToObservable<
subscriber.complete();
return () => {};
}
handler?.().then(t => {
subscriber.next(t);
});
handler?.()
.then(t => {
subscriber.next(t);
})
.catch(err => {
subscriber.error(err);
});
return event(t => {
subscriber.next(t);
});
@@ -92,20 +92,25 @@ const BlockSuiteEditorImpl = (props: EditorProps): ReactElement => {
return;
}
if (page.awarenessStore.getFlag('enable_block_hub')) {
editor.createBlockHub().then(blockHub => {
if (blockHubRef.current) {
blockHubRef.current.remove();
}
blockHubRef.current = blockHub;
const toolWrapper = document.querySelector('#toolWrapper');
if (!toolWrapper) {
console.warn(
'toolWrapper not found, block hub feature will not be available.'
);
} else {
toolWrapper.appendChild(blockHub);
}
});
editor
.createBlockHub()
.then(blockHub => {
if (blockHubRef.current) {
blockHubRef.current.remove();
}
blockHubRef.current = blockHub;
const toolWrapper = document.querySelector('#toolWrapper');
if (!toolWrapper) {
console.warn(
'toolWrapper not found, block hub feature will not be available.'
);
} else {
toolWrapper.appendChild(blockHub);
}
})
.catch(err => {
console.error(err);
});
}
container.appendChild(editor);
@@ -52,8 +52,8 @@ export const AffineSharePage: FC<ShareMenuProps> = props => {
const onClickCreateLink = useCallback(() => {
setIsPublic(true);
}, [setIsPublic]);
const onClickCopyLink = useCallback(() => {
navigator.clipboard.writeText(sharingUrl);
const onClickCopyLink = useCallback(async () => {
await navigator.clipboard.writeText(sharingUrl);
toast(t['Copied link to clipboard']());
}, [sharingUrl, t]);
const onDisablePublic = useCallback(() => {
@@ -32,8 +32,8 @@ describe('GraphQL fetcher', () => {
const gql = gqlFetcherFactory('https://example.com/graphql');
it('should send POST request to given endpoint', () => {
gql(
it('should send POST request to given endpoint', async () => {
await gql(
// @ts-expect-error variables is actually optional
{ query }
);
@@ -44,8 +44,8 @@ describe('GraphQL fetcher', () => {
expect(ctx.method).toBe('POST');
});
it('should send with correct graphql JSON body', () => {
gql({
it('should send with correct graphql JSON body', async () => {
await gql({
query,
// @ts-expect-error forgive the fake variables
variables: { a: 1, b: '2', c: { d: false } },
@@ -63,8 +63,8 @@ describe('GraphQL fetcher', () => {
`);
});
it('should correctly ignore nil variables', () => {
gql({
it('should correctly ignore nil variables', async () => {
await gql({
query,
// @ts-expect-error forgive the fake variables
variables: { a: false, b: null, c: undefined },
@@ -74,7 +74,7 @@ describe('GraphQL fetcher', () => {
'"{\\"query\\":\\"query { field }\\",\\"variables\\":{\\"a\\":false,\\"b\\":null},\\"operationName\\":\\"query\\"}"'
);
gql({
await gql({
query,
// @ts-expect-error forgive the fake variables
variables: { a: false, b: null, c: undefined },
+17 -9
View File
@@ -68,15 +68,23 @@ const standardizeLocale = (language: string) => {
export const createI18n = () => {
const i18n = i18next.createInstance();
i18n.use(initReactI18next).init({
lng: 'en',
fallbackLng,
debug: false,
resources,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
});
i18n
.use(initReactI18next)
.init({
lng: 'en',
fallbackLng,
debug: false,
resources,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
})
.then(() => {
console.info('i18n init success');
})
.catch(() => {
console.error('i18n init failed');
});
i18n.on('languageChanged', lng => {
localStorage.setItem(STORAGE_KEY, lng);
+6 -3
View File
@@ -47,9 +47,9 @@ const getBaseTranslations = async (baseLanguage: { tag: string }) => {
const main = async () => {
try {
fs.access(RES_DIR);
await fs.access(RES_DIR);
} catch (error) {
fs.mkdir(RES_DIR);
fs.mkdir(RES_DIR).catch(console.error);
console.log('Create directory', RES_DIR);
}
console.log('Loading project languages...');
@@ -149,4 +149,7 @@ const main = async () => {
console.log('Done');
};
main();
main().catch(e => {
console.error(e);
process.exit(1);
});
+4 -1
View File
@@ -152,4 +152,7 @@ const main = async () => {
// TODO send notification
};
main();
main().catch(e => {
console.error(e);
process.exit(1);
});
+8 -2
View File
@@ -40,7 +40,10 @@ export function definePlugin<ID extends string>(
blockSuiteAdapter
.load()
.then(({ default: adapter }) => updateAdapter(adapter));
.then(({ default: adapter }) => updateAdapter(adapter))
.catch(err => {
pluginLogger.error('[definePlugin] blockSuiteAdapter error', err);
});
if (import.meta.webpackHot) {
blockSuiteAdapter.hotModuleReload(async _ => {
@@ -64,7 +67,10 @@ export function definePlugin<ID extends string>(
uiAdapterLoader
.load()
.then(({ default: adapter }) => updateAdapter(adapter));
.then(({ default: adapter }) => updateAdapter(adapter))
.catch(err => {
pluginLogger.error('[definePlugin] blockSuiteAdapter error', err);
});
if (import.meta.webpackHot) {
uiAdapterLoader.hotModuleReload(async _ => {
@@ -37,6 +37,9 @@ fetch(new URL('@affine-test/fixtures/large-image.png', import.meta.url))
},
frameId
);
})
.catch(err => {
console.error('Failed to load large-image.png', err);
});
export const Default = () => {
@@ -54,6 +54,10 @@ fetch(new URL('@affine-test/fixtures/smile.png', import.meta.url))
new Blob([buffer], { type: 'image/png' })
);
avatarBlockSuiteWorkspace.meta.setAvatar(id);
})
.catch(() => {
// just ignore
console.error('Failed to load smile.png');
});
export const BlobExample: StoryFn<WorkspaceAvatarProps> = props => {
+33 -22
View File
@@ -1,3 +1,4 @@
import { DebugLogger } from '@affine/debug';
import type { BlobStorage } from '@blocksuite/store';
import { createIndexeddbStorage } from '@blocksuite/store';
import { openDB } from 'idb';
@@ -19,6 +20,8 @@ interface AffineBlob extends DBSchema {
// todo: migrate blob storage from `createIndexeddbStorage`
}
const logger = new DebugLogger('affine:blob');
export const createAffineBlobStorage = (
workspaceId: string,
workspaceApis: ReturnType<typeof createWorkspaceApis>
@@ -29,19 +32,25 @@ export const createAffineBlobStorage = (
db.createObjectStore('uploading', { keyPath: 'key' });
},
});
dbPromise.then(async db => {
const t = db.transaction('uploading', 'readwrite').objectStore('uploading');
await t.getAll().then(blobs =>
blobs.map(({ arrayBuffer, type }) =>
workspaceApis.uploadBlob(workspaceId, arrayBuffer, type).then(key => {
const t = db
.transaction('uploading', 'readwrite')
.objectStore('uploading');
return t.delete(key);
})
)
);
});
dbPromise
.then(async db => {
const t = db
.transaction('uploading', 'readwrite')
.objectStore('uploading');
await t.getAll().then(blobs =>
blobs.map(({ arrayBuffer, type }) =>
workspaceApis.uploadBlob(workspaceId, arrayBuffer, type).then(key => {
const t = db
.transaction('uploading', 'readwrite')
.objectStore('uploading');
return t.delete(key);
})
)
);
})
.catch(err => {
logger.error('[createAffineBlobStorage] dbPromise error', err);
});
return {
crud: {
get: async key => {
@@ -60,19 +69,21 @@ export const createAffineBlobStorage = (
.transaction('uploading', 'readwrite')
.objectStore('uploading');
let uploaded = false;
t.put({
await t.put({
key,
arrayBuffer,
type: value.type,
}).then(() => {
// delete the uploading blob after uploaded
if (uploaded) {
const t = db
.transaction('uploading', 'readwrite')
.objectStore('uploading');
t.delete(key);
}
});
// delete the uploading blob after uploaded
if (uploaded) {
const t = db
.transaction('uploading', 'readwrite')
.objectStore('uploading');
// don't await here, we don't care if it's deleted
t.delete(key).catch(err => {
logger.error('[createAffineBlobStorage] delete error', err);
});
}
await Promise.all([
storage.crud.set(key, value),
workspaceApis
@@ -28,7 +28,7 @@ describe('crud', () => {
});
test('delete not exist', async () => {
expect(async () =>
await expect(async () =>
CRUD.delete({
id: 'not_exist',
flavour: WorkspaceFlavour.LOCAL,
@@ -197,7 +197,7 @@ describe('indexeddb provider', () => {
test('cleanup when connecting', async () => {
const provider = createIndexedDBProvider(workspace.id, workspace.doc);
provider.connect();
expect(() => provider.cleanup()).rejects.toThrowError(
await expect(() => provider.cleanup()).rejects.toThrowError(
CleanupWhenConnectingError
);
await provider.whenSynced;
@@ -259,7 +259,7 @@ describe('indexeddb provider', () => {
yDoc.getMap().set('foo', 'bar');
const persistence = new IndexeddbPersistence('test', yDoc);
await persistence.whenSynced;
persistence.destroy();
await persistence.destroy();
}
{
const yDoc = new Doc();
@@ -274,7 +274,7 @@ describe('indexeddb provider', () => {
indexedDB.databases = vi.fn(async () => {
throw new Error('not supported');
});
expect(indexedDB.databases).rejects.toThrow('not supported');
await expect(indexedDB.databases).rejects.toThrow('not supported');
const yDoc = new Doc();
expect(indexedDB.databases).toBeCalledTimes(1);
const provider = createIndexedDBProvider('test', yDoc);
+1 -1
View File
@@ -286,7 +286,7 @@ export const createIndexedDBProvider = (
if (connected) {
throw new CleanupWhenConnectingError();
}
(await dbPromise).delete('workspace', id);
await (await dbPromise).delete('workspace', id);
},
whenSynced: Promise.resolve(),
get connected() {