From 041627f7e451b7a192b8a58a825ec70b80285e22 Mon Sep 17 00:00:00 2001 From: Aria Minaei Date: Thu, 3 Aug 2023 10:46:36 +0200 Subject: [PATCH] Fix the dangling promises --- .eslintrc.js | 1 + packages/benchmarks/src/index.tsx | 2 +- packages/browser-bundles/devEnv/build.ts | 4 +- packages/dataverse/devEnv/build.ts | 2 +- .../playground/src/shared/image/index.tsx | 12 +++-- .../src/shared/notifications/index.tsx | 4 +- .../playground/src/shared/theatric/index.tsx | 2 +- .../src/tests/r3f-stress-test/App.tsx | 2 +- .../src/tests/reading-obj-value/index.tsx | 4 +- packages/r3f/devEnv/bundle.ts | 5 +- packages/react/devEnv/build.ts | 6 +-- packages/theatric/devEnv/build.ts | 6 +-- theatre/core/src/projects/Project.ts | 45 +++++++++-------- theatre/core/src/sequences/TheatreSequence.ts | 5 +- theatre/devEnv/bundle.ts | 2 +- theatre/devEnv/watch.ts | 2 +- theatre/shared/src/testUtils.ts | 2 +- theatre/studio/src/Studio.ts | 50 ++++++++++++++----- theatre/studio/src/UI/UI.ts | 23 ++++++--- 19 files changed, 112 insertions(+), 67 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index ad524b5..d6a599c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -66,6 +66,7 @@ module.exports = { }, ], '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/no-floating-promises': 'warn', }, }, { diff --git a/packages/benchmarks/src/index.tsx b/packages/benchmarks/src/index.tsx index 11361c5..a13045a 100644 --- a/packages/benchmarks/src/index.tsx +++ b/packages/benchmarks/src/index.tsx @@ -106,6 +106,6 @@ async function test1() { iterateOnSequence() } -test1().then(() => { +void test1().then(() => { console.log('test1 done') }) diff --git a/packages/browser-bundles/devEnv/build.ts b/packages/browser-bundles/devEnv/build.ts index eb87eb0..f76894a 100644 --- a/packages/browser-bundles/devEnv/build.ts +++ b/packages/browser-bundles/devEnv/build.ts @@ -35,14 +35,14 @@ function createBundles(watch: boolean) { // format: 'iife', // }) - build({ + void build({ ...esbuildConfig, entryPoints: [path.join(pathToPackage, 'src/core-and-studio.ts')], outfile: path.join(pathToPackage, 'dist/core-and-studio.js'), format: 'iife', }) - build({ + void build({ ...esbuildConfig, entryPoints: [path.join(pathToPackage, 'src/core-only.ts')], outfile: path.join(pathToPackage, 'dist/core-only.min.js'), diff --git a/packages/dataverse/devEnv/build.ts b/packages/dataverse/devEnv/build.ts index 063e614..e0998be 100644 --- a/packages/dataverse/devEnv/build.ts +++ b/packages/dataverse/devEnv/build.ts @@ -17,7 +17,7 @@ function createBundles(watch: boolean) { conditions: ['browser', 'node'], } - build({ + void build({ ...esbuildConfig, outfile: path.join(pathToPackage, 'dist/index.js'), format: 'cjs', diff --git a/packages/playground/src/shared/image/index.tsx b/packages/playground/src/shared/image/index.tsx index 7b445de..e216aa1 100644 --- a/packages/playground/src/shared/image/index.tsx +++ b/packages/playground/src/shared/image/index.tsx @@ -53,7 +53,7 @@ const ImageTypeExample: React.FC<{}> = (props) => { onClick={() => { if (sheet.sequence.position === 0) { sheet.sequence.position = 0 - sheet.sequence.play() + void sheet.sequence.play() } else { sheet.sequence.position = 0 } @@ -64,6 +64,10 @@ const ImageTypeExample: React.FC<{}> = (props) => { ) } -project.ready.then(() => { - render(, document.getElementById('root')) -}) +project.ready + .then(() => { + render(, document.getElementById('root')) + }) + .catch((err) => { + console.error(err) + }) diff --git a/packages/playground/src/shared/notifications/index.tsx b/packages/playground/src/shared/notifications/index.tsx index 00bd26f..5d2f0d8 100644 --- a/packages/playground/src/shared/notifications/index.tsx +++ b/packages/playground/src/shared/notifications/index.tsx @@ -7,7 +7,7 @@ import {Scene} from './Scene' studio.initialize() // trigger warning notification -getProject('Sample project').sheet('Scene').sequence.play() +void getProject('Sample project').sheet('Scene').sequence.play() // fire an info notification notify.info( @@ -16,7 +16,7 @@ notify.info( '(and all others) at the start of index.tsx. You can also see examples of success and warnign notifications.', ) -getProject('Sample project').ready.then(() => { +void getProject('Sample project').ready.then(() => { // fire a success notification on project load notify.success( 'Project loaded!', diff --git a/packages/playground/src/shared/theatric/index.tsx b/packages/playground/src/shared/theatric/index.tsx index ce0cc13..6d2b273 100644 --- a/packages/playground/src/shared/theatric/index.tsx +++ b/packages/playground/src/shared/theatric/index.tsx @@ -3,7 +3,7 @@ import {render} from 'react-dom' import React, {useState} from 'react' import state from './state.json' -initialize({state}) +void initialize({state}) function SomeComponent({id}: {id: string}) { const {foo, $get, $set} = useControls( diff --git a/packages/playground/src/tests/r3f-stress-test/App.tsx b/packages/playground/src/tests/r3f-stress-test/App.tsx index c5a83ae..43ed8f7 100644 --- a/packages/playground/src/tests/r3f-stress-test/App.tsx +++ b/packages/playground/src/tests/r3f-stress-test/App.tsx @@ -76,7 +76,7 @@ function App() { const bg = bgs[bgIndex] const project = getProject('SpaceStress', {state}) const sheet = project.sheet('Scene') - project.ready.then(() => sheet.sequence.play({iterationCount: Infinity})) + void project.ready.then(() => sheet.sequence.play({iterationCount: Infinity})) const allPropsObj = sheet.object('All Props Tester', allPropsObjectConfig) console.log('allPropsObj', allPropsObj) diff --git a/packages/playground/src/tests/reading-obj-value/index.tsx b/packages/playground/src/tests/reading-obj-value/index.tsx index 4886def..16183ea 100644 --- a/packages/playground/src/tests/reading-obj-value/index.tsx +++ b/packages/playground/src/tests/reading-obj-value/index.tsx @@ -39,13 +39,13 @@ const elements = new Array(TOTAL_ELEMENTS).fill(0).map((_, idx) => { return {el, sheet, obj} }) -project.ready.then(() => { +void project.ready.then(() => { // select the playback controls obj so it shows as a tweakable control studio.setSelection([playbackControlObj]) for (let i = 0; i < elements.length; i++) { const sheet = elements[i].sheet sheet.sequence.position = i * TOTAL_ELEMENTS_R * 5 - sheet.sequence.play({ + void sheet.sequence.play({ iterationCount: Infinity, }) } diff --git a/packages/r3f/devEnv/bundle.ts b/packages/r3f/devEnv/bundle.ts index 7518abd..6a4b1be 100644 --- a/packages/r3f/devEnv/bundle.ts +++ b/packages/r3f/devEnv/bundle.ts @@ -8,11 +8,10 @@ const definedGlobals = { 'process.env.NODE_ENV': JSON.stringify('production'), } -createBundles() +void createBundles() async function createBundles() { - createMainBundle() - createExtensionBundle() + await Promise.all([createMainBundle(), createExtensionBundle()]) async function createMainBundle() { const pathToEntry = path.join(__dirname, '../src/index.ts') diff --git a/packages/react/devEnv/build.ts b/packages/react/devEnv/build.ts index 599d720..69c3856 100644 --- a/packages/react/devEnv/build.ts +++ b/packages/react/devEnv/build.ts @@ -24,7 +24,7 @@ const definedGlobals = { global: 'window', } -function createBundles(watch: boolean) { +async function createBundles(watch: boolean) { const pathToPackage = path.join(__dirname, '../') const esbuildConfig: Parameters[0] = { entryPoints: [path.join(pathToPackage, 'src/index.ts')], @@ -39,7 +39,7 @@ function createBundles(watch: boolean) { plugins: [externalPlugin([/^[\@a-zA-Z]+/])], } - build({ + await build({ ...esbuildConfig, outfile: path.join(pathToPackage, 'dist/index.js'), format: 'cjs', @@ -52,4 +52,4 @@ function createBundles(watch: boolean) { // }) } -createBundles(false) +void createBundles(false) diff --git a/packages/theatric/devEnv/build.ts b/packages/theatric/devEnv/build.ts index cb4c484..fad8649 100644 --- a/packages/theatric/devEnv/build.ts +++ b/packages/theatric/devEnv/build.ts @@ -24,7 +24,7 @@ const definedGlobals = { global: 'window', } -function createBundles(watch: boolean) { +async function createBundles(watch: boolean) { const pathToPackage = path.join(__dirname, '../') const pkgJson = require(path.join(pathToPackage, 'package.json')) const listOfDependencies = Object.keys(pkgJson.dependencies || {}) @@ -52,7 +52,7 @@ function createBundles(watch: boolean) { ], } - build({ + await build({ ...esbuildConfig, outfile: path.join(pathToPackage, 'dist/index.js'), format: 'cjs', @@ -65,4 +65,4 @@ function createBundles(watch: boolean) { // }) } -createBundles(false) +void createBundles(false) diff --git a/theatre/core/src/projects/Project.ts b/theatre/core/src/projects/Project.ts index 1b0e5c1..e4d5895 100644 --- a/theatre/core/src/projects/Project.ts +++ b/theatre/core/src/projects/Project.ts @@ -196,29 +196,34 @@ export default class Project { } this._studio = studio - studio.initialized.then(async () => { - await initialiseProjectState(studio, this, this.config.state) + studio.initialized + .then(async () => { + await initialiseProjectState(studio, this, this.config.state) - this._pointerProxies.historic.setPointer( - studio.atomP.historic.coreByProject[this.address.projectId], - ) - this._pointerProxies.ahistoric.setPointer( - studio.atomP.ahistoric.coreByProject[this.address.projectId], - ) - this._pointerProxies.ephemeral.setPointer( - studio.atomP.ephemeral.coreByProject[this.address.projectId], - ) + this._pointerProxies.historic.setPointer( + studio.atomP.historic.coreByProject[this.address.projectId], + ) + this._pointerProxies.ahistoric.setPointer( + studio.atomP.ahistoric.coreByProject[this.address.projectId], + ) + this._pointerProxies.ephemeral.setPointer( + studio.atomP.ephemeral.coreByProject[this.address.projectId], + ) - // asset storage has to be initialized after the pointers are set - studio - .createAssetStorage(this, this.config.assets?.baseUrl) - .then((assetStorage) => { - this.assetStorage = assetStorage - this._assetStorageReadyDeferred.resolve(undefined) - }) + // asset storage has to be initialized after the pointers are set + await studio + .createAssetStorage(this, this.config.assets?.baseUrl) + .then((assetStorage) => { + this.assetStorage = assetStorage + this._assetStorageReadyDeferred.resolve(undefined) + }) - this._studioReadyDeferred.resolve(undefined) - }) + this._studioReadyDeferred.resolve(undefined) + }) + .catch((err) => { + console.error(err) + throw err + }) } get isAttachedToStudio() { diff --git a/theatre/core/src/sequences/TheatreSequence.ts b/theatre/core/src/sequences/TheatreSequence.ts index 0121810..9c5d630 100644 --- a/theatre/core/src/sequences/TheatreSequence.ts +++ b/theatre/core/src/sequences/TheatreSequence.ts @@ -354,7 +354,9 @@ async function resolveAudioBuffer(args: IAttachAudioArgs): Promise<{ } return new Promise((resolve) => { const listener = () => { - ctx.resume() + ctx.resume().catch((err) => { + console.error(err) + }) } const eventsToHookInto: Array = [ @@ -412,6 +414,7 @@ async function resolveAudioBuffer(args: IAttachAudioArgs): Promise<{ const audioContext = await audioContextPromise + // eslint-disable-next-line @typescript-eslint/no-floating-promises audioContext.decodeAudioData( arrayBuffer, decodedBufferDeferred.resolve, diff --git a/theatre/devEnv/bundle.ts b/theatre/devEnv/bundle.ts index 5e3b9df..7396b41 100644 --- a/theatre/devEnv/bundle.ts +++ b/theatre/devEnv/bundle.ts @@ -1,3 +1,3 @@ import {createBundles} from './createBundles' -createBundles(false) +void createBundles(false) diff --git a/theatre/devEnv/watch.ts b/theatre/devEnv/watch.ts index edd9759..71de62a 100644 --- a/theatre/devEnv/watch.ts +++ b/theatre/devEnv/watch.ts @@ -1,3 +1,3 @@ import {createBundles} from './createBundles' -createBundles(true) +void createBundles(true) diff --git a/theatre/shared/src/testUtils.ts b/theatre/shared/src/testUtils.ts index 9c51b75..bc3d443 100644 --- a/theatre/shared/src/testUtils.ts +++ b/theatre/shared/src/testUtils.ts @@ -28,7 +28,7 @@ const defaultProps = { let lastProjectN = 0 const studio = getStudio()! -studio.initialize({usePersistentStorage: false}) +void studio.initialize({usePersistentStorage: false}) export async function setupTestSheet(sheetState: SheetState_Historic) { const projectState: ProjectState_Historic = { diff --git a/theatre/studio/src/Studio.ts b/theatre/studio/src/Studio.ts index e1bd052..12ddb0d 100644 --- a/theatre/studio/src/Studio.ts +++ b/theatre/studio/src/Studio.ts @@ -203,7 +203,9 @@ export class Studio { if (process.env.NODE_ENV !== 'test') { this.ui.render() - checkForUpdates() + checkForUpdates().catch((err) => { + console.error(err) + }) } } @@ -266,7 +268,7 @@ export class Studio { return this._coreAtom.pointer.core } - extend(extension: IExtension) { + extend(extension: IExtension, opts?: {__experimental_reconfigure?: boolean}) { if (!extension || typeof extension !== 'object') { throw new Error(`Extensions must be JS objects`) } @@ -275,20 +277,27 @@ export class Studio { throw new Error(`extension.id must be a string`) } + const reconfigure = opts?.__experimental_reconfigure === true + const extensionId = extension.id const prevExtension = this._store.getState().ephemeral.extensions.byId[extensionId] if (prevExtension) { - if ( - extension === prevExtension || - shallowEqual(extension, prevExtension) - ) { - // probably running studio.extend() several times because of hot reload. - // as long as it's the same extension, we can safely ignore. - return + if (reconfigure) { + } else { + if ( + extension === prevExtension || + shallowEqual(extension, prevExtension) + ) { + // probably running studio.extend() several times because of hot reload. + // as long as it's the same extension, we can safely ignore. + return + } + throw new Error( + `Extension id "${extension.id}" is already defined. If you mean to re-configure the extension, do it like this: studio.extend(extension, {__experimental_reconfigure: true})})`, + ) } - throw new Error(`Extension id "${extension.id}" is already defined`) } this.transaction(({drafts}) => { @@ -296,6 +305,14 @@ export class Studio { const allPaneClasses = drafts.ephemeral.extensions.paneClasses + if (reconfigure && prevExtension) { + // remove all pane classes that were set by the previous version of the extension + prevExtension.panes?.forEach((classDefinition) => { + delete allPaneClasses[classDefinition.class] + }) + } + + // if the extension defines pane classes, add them to the list of all pane classes extension.panes?.forEach((classDefinition) => { if (typeof classDefinition.class !== 'string') { throw new Error(`pane.class must be a string`) @@ -309,9 +326,16 @@ export class Studio { const existing = allPaneClasses[classDefinition.class] if (existing) { - throw new Error( - `Pane class "${classDefinition.class}" already exists and is supplied by extension ${existing}`, - ) + if (reconfigure && existing.extensionId === extension.id) { + // well this should never happen because we already deleted the pane class above + console.warn( + `Pane class "${classDefinition.class}" already exists. This is a bug in Theatre.js. Please report it at https://github.com/theatre-js/theatre/issues/new`, + ) + } else { + throw new Error( + `Pane class "${classDefinition.class}" already exists and is supplied by extension ${existing}`, + ) + } } allPaneClasses[classDefinition.class] = { diff --git a/theatre/studio/src/UI/UI.ts b/theatre/studio/src/UI/UI.ts index b7d9704..4ecd79d 100644 --- a/theatre/studio/src/UI/UI.ts +++ b/theatre/studio/src/UI/UI.ts @@ -24,9 +24,14 @@ export default class UI { } this._rendered = true - this._nonSSRBits.then((b) => { - b.render() - }) + this._nonSSRBits + .then((b) => { + b.render() + }) + .catch((err) => { + console.error(err) + throw err + }) } hide() { @@ -53,10 +58,14 @@ export default class UI { let unmount: null | (() => void) = null - this._nonSSRBits.then((nonSSRBits) => { - if (shouldUnmount) return // unmount requested before the toolset is mounted, so, abort - unmount = nonSSRBits.renderToolset(toolsetId, htmlNode) - }) + this._nonSSRBits + .then((nonSSRBits) => { + if (shouldUnmount) return // unmount requested before the toolset is mounted, so, abort + unmount = nonSSRBits.renderToolset(toolsetId, htmlNode) + }) + .catch((err) => { + console.error(err) + }) return () => { if (unmount) {