enso/app/ide-desktop/lib/client/watch.ts
somebody1234 ce33af82f7
Split dashboard.tsx into smaller components (#6546)
* Consistent order for statements in `dashboard.tsx`; change functions back to lambdas

* Create convenience aliases for each asset type

* Remove obsolete FIXME

* Refactor out column renderers into components

* Enable `prefer-const` lint

* Remove hardcoded product name

* Add fixme

* Enable `react-hooks` lints (not working for some reason)

* Consistent messages for naming-convention lint overrides

* Enable `react` lints

* Extract out tables for each asset type

* Refactor out column display mode switcher to a file

* Switch VM check state to use an enum

* Fix lint errors

* Minor section change

* Fix position of create forms

* Fix bugs; improve debugging QoL

* Add documentation for new components

* Refactor out drive bar

* Refactor out event handlers to variables

* Minor clarifications

* Refactor out directory view; some fixes; improve React DX

There are still many issues when switching backends

* Add `assert`

* Use `backend.platform` instead of checking for properties

* Fix errors when switching backend

* Minor style changes; fix lint errors

* Fix assert behavior

* Change `Rows` to `Table`

* Fixes

* Fix lint errors

* Fix "show dashboard" button

* Implement click to rename

* Fix lint errors

* Fix lint errors (along with a bug in `devServiceWorker`)

* Enable dev-mode on `ide watch`

* Fix bug in `useAsyncEffect` introduced during merge

* More fixes; new debug hooks; fix infinite loop in `auth.tsx`

* Inline Cognito methods

* Remove redundant `Promise.resolve`s

* Fix column display

* Fixes

* Simplify modal type

* Fix bug when opening IDE

* Shift+click to select a range of table items

* Implement delete multiple

* Fixes

* Tick and cross for rename input; fixes

* Implement rename and delete directory and multi-delete directory; fixes

* Optimize modal re-rendering

* Make some internal `Props` private

* Remove old asset selection code

* Eliminate re-renders when clicking document body

* Fix name flickering when renaming

* Use static placeholders

* Avoid refreshing entire directory on rename

* Use asset name instead of ID in error messages

* QoL improvements

* Enable react lints and `strict-boolean-expressions`

* Extract dashboard feature flags to its own module

* Feature flag to show more toasts; minimize calls to `listDirectory`

* Deselect selection on delete; hide unused features; add exception to PascalCase lint

* Fix projects disappearing after being created

* Fix name of `projectEvent` module imports

* Re-disable delete when project is being closed

* Fix assets refreshing when adding new projects

* Refactor row state into `Table`; fix delete not being disabled again

* Address review

* Implement shortcut registry

* Fix stop icon spinning when switching backends (ported from #6919)

* Give columns names

* Immediately show project as opening

* Replace `asNewtype` with constructor functions

* Address review

* Minor bugfixes

* Prepare for optimistically updated tables

* wip 2

* Fix type errors

* Remove indirect usages of `doRefresh`

Updating the lists of items will need to be re-added later

* Remove `toastPromise`

* Fix `New_Directory_-Infinity` bug

* wip

* WIP: Begin restoring functionality to rows

* Fix most issues with DirectoriesTable

* Port optimistic UI from `DirectoriesTable` to all other asset tables

* Fix bugs in item list events for asset tables

* Merge `projectActionButton` into `projectsTable`

* Remove `RenameModal`; minor context menu bugfixes

* Fix bugs

* Remove small default user icon

* Fix more bugs

* Fix bugs

* Fix type error

* Address review and QA

* Fix optimistic UI for "manage permissions" modal

* Fix "share with" modal

* Fix template spinner disappearing

* Allow multiple projects to be opened on local backend; fix version lifecycle returned by local backend

* Fix minor bug when closing local project

---------

Co-authored-by: Paweł Buchowski <pawel.buchowski@enso.org>
2023-07-19 11:48:39 +02:00

158 lines
5.9 KiB
TypeScript

/** @file This script is for watching the whole IDE and spawning the electron process.
*
* It sets up watchers for the client and content, and spawns the electron process with the IDE.
* The spawned electron process can then use its refresh capability to pull the latest changes
* from the watchers.
*
* If the electron app is closed, the script will restart it, allowing to test the IDE setup.
* To stop, use Ctrl+C. */
import * as childProcess from 'node:child_process'
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import * as url from 'node:url'
import process from 'node:process'
import * as esbuild from 'esbuild'
import * as clientBundler from './esbuild-config'
import * as contentBundler from '../content/esbuild-config'
import * as dashboardBundler from '../dashboard/esbuild-config'
import * as paths from './paths'
// =============
// === Types ===
// =============
/** Set of esbuild watches for the client and content. */
interface Watches {
client: esbuild.BuildResult
dashboard: esbuild.BuildResult
content: esbuild.BuildResult
}
// =================
// === Constants ===
// =================
/** The path of this file. */
const THIS_PATH = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)))
const IDE_DIR_PATH = paths.getIdeDirectory()
const PROJECT_MANAGER_BUNDLE_PATH = paths.getProjectManagerBundlePath()
// =============
// === Watch ===
// =============
console.log('Cleaning IDE dist directory.')
await fs.rm(IDE_DIR_PATH, { recursive: true, force: true })
await fs.mkdir(IDE_DIR_PATH, { recursive: true })
const ALL_BUNDLES_READY = new Promise<Watches>((resolve, reject) => {
void (async () => {
console.log('Bundling client.')
const clientBundlerOpts = clientBundler.bundlerOptionsFromEnv()
clientBundlerOpts.outdir = path.resolve(IDE_DIR_PATH)
// Eslint is wrong here; `clientBundlerOpts.plugins` is actually `undefined`.
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
;(clientBundlerOpts.plugins ??= []).push({
name: 'enso-on-rebuild',
setup: build => {
build.onEnd(result => {
if (result.errors.length) {
// We cannot carry on if the client failed to build, because electron
// would immediately exit with an error.
console.error('Client watch bundle failed:', result.errors[0])
reject(result.errors[0])
} else {
console.log('Client bundle updated.')
}
})
},
})
const clientBuilder = await esbuild.context(clientBundlerOpts)
const client = await clientBuilder.rebuild()
console.log('Result of client bundling: ', client)
void clientBuilder.watch()
console.log('Bundling dashboard.')
const dashboardOpts = dashboardBundler.bundleOptions()
dashboardOpts.plugins.push({
name: 'enso-on-rebuild',
setup: build => {
build.onEnd(() => {
console.log('Dashboard bundle updated.')
})
},
})
dashboardOpts.outdir = path.resolve(IDE_DIR_PATH, 'assets')
const dashboardBuilder = await esbuild.context(dashboardOpts)
const dashboard = await dashboardBuilder.rebuild()
console.log('Result of dashboard bundling: ', dashboard)
// We do not need to serve the dashboard as it outputs to the same directory.
// It will not rebuild on request, but it is not intended to rebuild on request anyway.
// This MUST be called before `builder.watch()` as `tailwind.css` must be generated
// before the copy plugin runs.
void dashboardBuilder.watch()
console.log('Bundling content.')
const contentOpts = contentBundler.bundlerOptionsFromEnv({
devMode: true,
supportsLocalBackend: true,
supportsDeepLinks: false,
})
contentOpts.plugins.push({
name: 'enso-on-rebuild',
setup: build => {
build.onEnd(() => {
console.log('Content bundle updated.')
})
},
})
contentOpts.pure.splice(contentOpts.pure.indexOf('assert'), 1)
;(contentOpts.inject = contentOpts.inject ?? []).push(
path.resolve(THIS_PATH, '..', '..', 'debugGlobals.ts')
)
contentOpts.outdir = path.resolve(IDE_DIR_PATH, 'assets')
contentOpts.define.REDIRECT_OVERRIDE = JSON.stringify('http://localhost:8080')
const contentBuilder = await esbuild.context(contentOpts)
const content = await contentBuilder.rebuild()
console.log('Result of content bundling: ', content)
void contentBuilder.watch()
resolve({ client, dashboard, content })
})()
})
await ALL_BUNDLES_READY
console.log('Exposing Project Manager bundle.')
await fs.symlink(
PROJECT_MANAGER_BUNDLE_PATH,
path.join(IDE_DIR_PATH, paths.PROJECT_MANAGER_BUNDLE),
'dir'
)
const ELECTRON_ARGS = [path.join(IDE_DIR_PATH, 'index.cjs'), '--', ...process.argv.slice(2)]
process.on('SIGINT', () => {
console.log('SIGINT received. Exiting.')
// The `esbuild` process seems to remain alive at this point and will keep our process
// from ending. Thus, we exit manually. It seems to terminate the child `esbuild` process
// as well.
process.exit(0)
})
while (true) {
console.log('Spawning Electron process.')
const electronProcess = childProcess.spawn('electron', ELECTRON_ARGS, {
stdio: 'inherit',
shell: true,
})
console.log('Waiting for Electron process to finish.')
const result = await new Promise((resolve, reject) => {
electronProcess.on('close', resolve)
electronProcess.on('error', reject)
})
console.log('Electron process finished. Exit code: ', result)
}