From de9f2764f9b69ee923d106e89c4adb44d0e24332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Wawrzyniec=20Urba=C5=84czyk?= Date: Mon, 18 Mar 2024 13:18:18 +0100 Subject: [PATCH] Remove `ensogl-pack` (#9407) This PR removes enso-pack (ensogl-pack) crate. It still keeps the `enso-runner` JS package, as it is used for CLI argument parser and logger. The runner should be probably refactored (and possible removed altogether). # Important Notes I've temporarily extracted the `enso-runner` to `lib/js` directory, as I wanted to avoid keeping pure JS library under `lib/rust`. Attempts at integrating this with `app/ide-desktop` and family caused too much trouble for this PR. The expectation is that the package will be removed or moved elsewhere soon anyway. --- Cargo.lock | 27 - Cargo.toml | 1 - app/ide-desktop/lib/client/package.json | 5 +- app/ide-desktop/lib/client/watch.ts | 150 ------ app/ide-desktop/lib/content/bundle.ts | 16 - app/ide-desktop/lib/content/esbuild-config.ts | 198 -------- app/ide-desktop/lib/content/globals.d.ts | 19 - app/ide-desktop/lib/content/package.json | 57 --- .../lib/content/src/devServiceWorker.ts | 64 --- app/ide-desktop/lib/content/src/entrypoint.ts | 348 ------------- app/ide-desktop/lib/content/src/index.html | 62 --- app/ide-desktop/lib/content/src/remoteLog.ts | 77 --- app/ide-desktop/lib/content/src/run.js | 10 - .../lib/content/src/serviceWorker.ts | 38 -- .../lib/content/src/serviceWorkerConstants.js | 59 --- app/ide-desktop/lib/content/src/style.css | 262 ---------- .../lib/content/src/wasm_imports.js | 11 - app/ide-desktop/lib/content/start.ts | 36 -- app/ide-desktop/lib/content/tsconfig.json | 5 - app/ide-desktop/lib/content/watch.ts | 61 --- app/ide-desktop/lib/dashboard/package.json | 1 + app/ide-desktop/lib/icons/package.json | 4 +- build/build/Cargo.toml | 1 - .../enso-pack/js => js/runner}/.eslintrc.cjs | 0 .../enso-pack/js => js/runner}/package.json | 0 .../js => js/runner}/src/runner/animation.ts | 0 .../js => js/runner}/src/runner/config.json | 0 .../js => js/runner}/src/runner/config.ts | 0 .../js => js/runner}/src/runner/data/array.ts | 0 .../runner}/src/runner/debug/help-screen.ts | 0 .../runner}/src/runner/debug/index.ts | 0 .../runner}/src/runner/debug/package-info.ts | 0 .../js => js/runner}/src/runner/dom/dom.ts | 0 .../js => js/runner}/src/runner/dom/logo.ts | 0 .../js => js/runner}/src/runner/dom/svg.ts | 0 .../js => js/runner}/src/runner/host.ts | 0 .../js => js/runner}/src/runner/index.ts | 0 .../js => js/runner}/src/runner/log/index.ts | 0 .../js => js/runner}/src/runner/log/logger.ts | 0 .../js => js/runner}/src/runner/log/router.ts | 0 .../js => js/runner}/src/runner/math.ts | 0 .../js => js/runner}/src/runner/name.ts | 0 .../runner}/src/runner/wasm/entry-point.ts | 0 .../js => js/runner}/src/runner/wasm/index.ts | 0 .../runner}/src/runner/wasm/loader.ts | 0 .../enso-pack/js => js/runner}/tsconfig.json | 0 lib/rust/enso-pack/Cargo.lock | 7 - lib/rust/enso-pack/Cargo.toml | 18 - .../enso-pack/js/src/asset-extractor/args.ts | 117 ----- .../js/src/asset-extractor/asset-extractor.ts | 70 --- .../enso-pack/js/src/asset-extractor/fs.ts | 130 ----- .../js/src/runtime-libs/runtime-libs.ts | 8 - .../js/src/wasm-pack-bundle/index.ts | 11 - lib/rust/enso-pack/src/assets.rs | 401 --------------- lib/rust/enso-pack/src/lib.rs | 464 ------------------ package-lock.json | 346 ++++++++++--- package.json | 4 +- 57 files changed, 288 insertions(+), 2800 deletions(-) delete mode 100644 app/ide-desktop/lib/client/watch.ts delete mode 100644 app/ide-desktop/lib/content/bundle.ts delete mode 100644 app/ide-desktop/lib/content/esbuild-config.ts delete mode 100644 app/ide-desktop/lib/content/globals.d.ts delete mode 100644 app/ide-desktop/lib/content/package.json delete mode 100644 app/ide-desktop/lib/content/src/devServiceWorker.ts delete mode 100644 app/ide-desktop/lib/content/src/entrypoint.ts delete mode 100644 app/ide-desktop/lib/content/src/index.html delete mode 100644 app/ide-desktop/lib/content/src/remoteLog.ts delete mode 100644 app/ide-desktop/lib/content/src/run.js delete mode 100644 app/ide-desktop/lib/content/src/serviceWorker.ts delete mode 100644 app/ide-desktop/lib/content/src/serviceWorkerConstants.js delete mode 100644 app/ide-desktop/lib/content/src/style.css delete mode 100644 app/ide-desktop/lib/content/src/wasm_imports.js delete mode 100644 app/ide-desktop/lib/content/start.ts delete mode 100644 app/ide-desktop/lib/content/tsconfig.json delete mode 100644 app/ide-desktop/lib/content/watch.ts rename lib/{rust/enso-pack/js => js/runner}/.eslintrc.cjs (100%) rename lib/{rust/enso-pack/js => js/runner}/package.json (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/animation.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/config.json (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/config.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/data/array.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/debug/help-screen.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/debug/index.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/debug/package-info.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/dom/dom.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/dom/logo.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/dom/svg.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/host.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/index.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/log/index.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/log/logger.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/log/router.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/math.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/name.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/wasm/entry-point.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/wasm/index.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/src/runner/wasm/loader.ts (100%) rename lib/{rust/enso-pack/js => js/runner}/tsconfig.json (100%) delete mode 100644 lib/rust/enso-pack/Cargo.lock delete mode 100644 lib/rust/enso-pack/Cargo.toml delete mode 100644 lib/rust/enso-pack/js/src/asset-extractor/args.ts delete mode 100644 lib/rust/enso-pack/js/src/asset-extractor/asset-extractor.ts delete mode 100644 lib/rust/enso-pack/js/src/asset-extractor/fs.ts delete mode 100644 lib/rust/enso-pack/js/src/runtime-libs/runtime-libs.ts delete mode 100644 lib/rust/enso-pack/js/src/wasm-pack-bundle/index.ts delete mode 100644 lib/rust/enso-pack/src/assets.rs delete mode 100644 lib/rust/enso-pack/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 4c865a9178..0199740d4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1199,7 +1199,6 @@ dependencies = [ "enso-build-macros-lib", "enso-enso-font", "enso-font", - "enso-pack", "futures", "futures-util", "glob", @@ -1414,20 +1413,6 @@ dependencies = [ "serde", ] -[[package]] -name = "enso-pack" -version = "0.1.0" -dependencies = [ - "enso-prelude", - "futures", - "ide-ci", - "manifest-dir-macros", - "serde", - "serde_json", - "tokio", - "walkdir", -] - [[package]] name = "enso-parser" version = "0.1.0" @@ -2513,18 +2498,6 @@ dependencies = [ "tokio-stream", ] -[[package]] -name = "manifest-dir-macros" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f08150cf2bab1fc47c2196f4f41173a27fcd0f684165e5458c0046b53a472e2f" -dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "syn 1.0.107", -] - [[package]] name = "matchers" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index f272335ad5..49bcba5995 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,6 @@ members = [ "lib/rust/parser/generate-java", "lib/rust/parser/schema", "lib/rust/parser/debug", - "lib/rust/enso-pack", "tools/language-server/logstat", "tools/language-server/wstest", ] diff --git a/app/ide-desktop/lib/client/package.json b/app/ide-desktop/lib/client/package.json index 116b18b5ab..d1ebe055b6 100644 --- a/app/ide-desktop/lib/client/package.json +++ b/app/ide-desktop/lib/client/package.json @@ -41,6 +41,8 @@ "esbuild": "^0.19.3", "fast-glob": "^3.2.12", "portfinder": "^1.0.32", + "sharp": "^0.31.2", + "to-ico": "^1.1.5", "tsx": "^4.7.1" }, "optionalDependencies": { @@ -53,7 +55,6 @@ "typecheck": "npm run --workspace=enso-gui2 compile-server && tsc --build", "start": "tsx start.ts", "build": "tsx bundle.ts", - "dist": "tsx dist.ts", - "watch": "tsx watch.ts" + "dist": "tsx dist.ts" } } diff --git a/app/ide-desktop/lib/client/watch.ts b/app/ide-desktop/lib/client/watch.ts deleted file mode 100644 index 6e9949e229..0000000000 --- a/app/ide-desktop/lib/client/watch.ts +++ /dev/null @@ -1,150 +0,0 @@ -/** @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 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 { - readonly client: esbuild.BuildResult - readonly dashboard: esbuild.BuildResult - readonly content: esbuild.BuildResult -} - -// ================= -// === Constants === -// ================= - -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((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({ - 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.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) -} diff --git a/app/ide-desktop/lib/content/bundle.ts b/app/ide-desktop/lib/content/bundle.ts deleted file mode 100644 index f7d300dd0d..0000000000 --- a/app/ide-desktop/lib/content/bundle.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** @file Entry point for the bundler. */ -import * as esbuild from 'esbuild' - -import * as bundler from './esbuild-config' - -// ======================= -// === Generate bundle === -// ======================= - -try { - const options = bundler.bundleOptions({ supportsLocalBackend: true, supportsDeepLinks: true }) - void esbuild.build(options) -} catch (error) { - console.error(error) - throw error -} diff --git a/app/ide-desktop/lib/content/esbuild-config.ts b/app/ide-desktop/lib/content/esbuild-config.ts deleted file mode 100644 index 6c91a9401e..0000000000 --- a/app/ide-desktop/lib/content/esbuild-config.ts +++ /dev/null @@ -1,198 +0,0 @@ -/** @file Configuration for the esbuild bundler and build/watch commands. - * - * The bundler processes each entry point into a single file, each with no external dependencies and - * minified. This primarily involves resolving all imports, along with some other transformations - * (like TypeScript compilation). - * - * See the bundlers documentation for more information: - * https://esbuild.github.io/getting-started/#bundling-for-node. */ - -import * as childProcess from 'node:child_process' -import * as fs from 'node:fs/promises' -import * as fsSync from 'node:fs' -import * as pathModule from 'node:path' -import * as url from 'node:url' - -import type * as esbuild from 'esbuild' -import * as esbuildPluginNodeGlobals from '@esbuild-plugins/node-globals-polyfill' -import * as esbuildPluginNodeModules from '@esbuild-plugins/node-modules-polyfill' -import esbuildPluginCopyDirectories from 'esbuild-plugin-copy-directories' -import esbuildPluginTime from 'esbuild-plugin-time' -import esbuildPluginYaml from 'esbuild-plugin-yaml' - -import * as appConfig from 'enso-common/src/appConfig' -import * as buildUtils from 'enso-common/src/buildUtils' -import BUILD_INFO from '../../../../build.json' assert { type: 'json' } - -// ================= -// === Constants === -// ================= - -const THIS_PATH = pathModule.resolve(pathModule.dirname(url.fileURLToPath(import.meta.url))) - -// ==================== -// === Global setup === -// ==================== - -await appConfig.readEnvironmentFromFile() - -// ============================= -// === Environment variables === -// ============================= - -/** Arguments that must always be supplied, because they are not defined as - * environment variables. */ -export interface PassthroughArguments { - /** Whether the application may have the local backend running. */ - readonly supportsLocalBackend: boolean - /** Whether the application supports deep links. This is only true when using - * the installed app on macOS and Windows. */ - readonly supportsDeepLinks: boolean -} - -/** Mandatory build options. */ -export interface Arguments extends PassthroughArguments { - /** List of files to be copied from WASM artifacts. */ - readonly wasmArtifacts: string - /** Directory with assets. Its contents are to be copied. */ - readonly assetsPath: string - /** Path where bundled files are output. */ - readonly outputPath: string -} - -/** Get arguments from the environment. */ -export function argumentsFromEnv(passthroughArguments: PassthroughArguments): Arguments { - const wasmArtifacts = buildUtils.requireEnv('ENSO_BUILD_GUI_WASM_ARTIFACTS') - const assetsPath = buildUtils.requireEnv('ENSO_BUILD_GUI_ASSETS') - const outputPath = pathModule.resolve(buildUtils.requireEnv('ENSO_BUILD_GUI'), 'assets') - return { ...passthroughArguments, wasmArtifacts, assetsPath, outputPath } -} - -// =================== -// === Git process === -// =================== - -/** Get output of a git command. - * @param command - Command line following the `git` program. - * @returns Output of the command. */ -function git(command: string): string { - // TODO [mwu] Eventually this should be removed, data should be provided by the build script - // through `BUILD_INFO`. The bundler configuration should not invoke git, - // it is not its responsibility. - return childProcess.execSync(`git ${command}`, { encoding: 'utf8' }).trim() -} - -// ================ -// === Bundling === -// ================ - -/** Generate the builder options. */ -export function bundlerOptions(args: Arguments) { - const { outputPath, wasmArtifacts, assetsPath, supportsLocalBackend, supportsDeepLinks } = args - const buildOptions = { - // The names come from a third-party API and cannot be changed. - /* eslint-disable @typescript-eslint/naming-convention */ - absWorkingDir: THIS_PATH, - bundle: true, - loader: { - '.html': 'copy', - '.css': 'copy', - '.map': 'copy', - '.wasm': 'copy', - '.svg': 'dataurl', - '.png': 'file', - '.jpg': 'file', - '.ttf': 'copy', - }, - entryPoints: [ - pathModule.resolve(THIS_PATH, 'src', 'entrypoint.ts'), - pathModule.resolve(THIS_PATH, 'src', 'index.html'), - pathModule.resolve(THIS_PATH, 'src', 'run.js'), - pathModule.resolve(THIS_PATH, 'src', 'style.css'), - pathModule.resolve(THIS_PATH, 'src', 'serviceWorker.ts'), - ...wasmArtifacts.split(pathModule.delimiter), - ...fsSync - .readdirSync(assetsPath) - .map(fileName => pathModule.resolve(assetsPath, fileName)), - ].map(path => ({ in: path, out: pathModule.basename(path, pathModule.extname(path)) })), - outdir: outputPath, - outbase: 'src', - plugins: [ - { - name: 'override-loaders', - setup: build => { - // This file MUST be in CommonJS format because it is loaded using `Function()` - // in `ensogl/pack/js/src/runner/index.ts`. - // All other files are ESM because of `"type": "module"` in `package.json`. - build.onLoad({ filter: /[/\\]pkg\.js$/ }, async info => { - const { path } = info - return { - contents: await fs.readFile(path), - loader: 'copy', - } - }) - // `.png` and `.svg` files not in the `assets` module should not use the `file` - // loader. - build.onLoad({ filter: /(?:\.png|\.svg)$/ }, async info => { - const { path } = info - if (!/[/\\]assets[/\\][^/\\]*(?:\.png|\.svg)$/.test(path)) { - return { - contents: await fs.readFile(path), - loader: 'copy', - } - } else { - return - } - }) - }, - }, - esbuildPluginCopyDirectories(), - esbuildPluginYaml.yamlPlugin({}), - esbuildPluginNodeModules.NodeModulesPolyfillPlugin(), - esbuildPluginNodeGlobals.NodeGlobalsPolyfillPlugin({ buffer: true, process: true }), - esbuildPluginTime(), - ], - define: { - GIT_HASH: JSON.stringify(git('rev-parse HEAD')), - GIT_STATUS: JSON.stringify(git('status --short --porcelain')), - BUILD_INFO: JSON.stringify(BUILD_INFO), - SUPPORTS_LOCAL_BACKEND: JSON.stringify(supportsLocalBackend), - SUPPORTS_DEEP_LINKS: JSON.stringify(supportsDeepLinks), - ...appConfig.getDefines(), - }, - pure: ['assert'], - sourcemap: true, - metafile: true, - format: 'esm', - platform: 'browser', - color: true, - logOverride: { - // Happens in Emscripten-generated MSDF (msdfgen_wasm.js): - // 1 │ ...typeof module!=="undefined"){module["exports"]=Module}process["o... - 'commonjs-variable-in-esm': 'silent', - // Happens in Emscripten-generated MSDF (msdfgen_wasm.js): - // 1 │ ...y{table.grow(1)}catch(err){if(!err instanceof RangeError){throw ... - 'suspicious-boolean-not': 'silent', - }, - /* eslint-enable @typescript-eslint/naming-convention */ - } satisfies esbuild.BuildOptions - // The narrower type is required to avoid non-null assertions elsewhere. - // The intersection with `esbuild.BuildOptions` is required to allow mutation. - const correctlyTypedBuildOptions: esbuild.BuildOptions & typeof buildOptions = buildOptions - return correctlyTypedBuildOptions -} - -/** The basic, common settings for the bundler, based on the environment variables. - * - * Note that they should be further customized as per the needs of the specific workflow - * (e.g. watch vs. build). */ -export function bundlerOptionsFromEnv(passthroughArguments: PassthroughArguments) { - return bundlerOptions(argumentsFromEnv(passthroughArguments)) -} - -/** esbuild options for bundling the package for a one-off build. - * - * Relies on the environment variables to be set. */ -export function bundleOptions(passthroughArguments: PassthroughArguments) { - return bundlerOptionsFromEnv(passthroughArguments) -} diff --git a/app/ide-desktop/lib/content/globals.d.ts b/app/ide-desktop/lib/content/globals.d.ts deleted file mode 100644 index e091d4322b..0000000000 --- a/app/ide-desktop/lib/content/globals.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** @file Globals defined only in this module. */ - -// ===================================== -// === Global namespace augmentation === -// ===================================== - -declare global { - // These are top-level constants, and therefore should be `CONSTANT_CASE`. - /* eslint-disable @typescript-eslint/naming-convention */ - /** Whether the */ - /** Whether the application may have the local backend running. */ - const SUPPORTS_LOCAL_BACKEND: boolean - /** Whether the application supports deep links. This is only true when using - * the installed app on macOS and Windows. */ - const SUPPORTS_DEEP_LINKS: boolean - /* eslint-enable @typescript-eslint/naming-convention */ -} - -export {} diff --git a/app/ide-desktop/lib/content/package.json b/app/ide-desktop/lib/content/package.json deleted file mode 100644 index 5ae09e8f2a..0000000000 --- a/app/ide-desktop/lib/content/package.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "name": "enso-content", - "version": "1.0.0", - "type": "module", - "author": { - "name": "Enso Team", - "email": "contact@enso.org" - }, - "homepage": "https://github.com/enso-org/ide", - "repository": { - "type": "git", - "url": "git@github.com:enso-org/ide.git" - }, - "bugs": { - "url": "https://github.com/enso-org/ide/issues" - }, - "scripts": { - "typecheck": "tsc --build", - "build": "tsx bundle.ts", - "watch": "tsx watch.ts", - "start": "tsx start.ts" - }, - "dependencies": { - "@types/semver": "^7.3.9", - "enso-content-config": "^1.0.0", - "react-toastify": "^9.1.3" - }, - "devDependencies": { - "@esbuild-plugins/node-globals-polyfill": "^0.2.3", - "@esbuild-plugins/node-modules-polyfill": "^0.2.2", - "@eslint/js": "^8.49.0", - "@types/connect": "^3.4.35", - "@types/morgan": "^1.9.4", - "@types/serve-static": "^1.15.1", - "@types/sharp": "^0.31.1", - "@types/to-ico": "^1.1.1", - "@types/ws": "^8.5.4", - "@typescript-eslint/eslint-plugin": "^6.7.2", - "@typescript-eslint/parser": "^6.7.2", - "enso-dashboard": "^0.1.0", - "esbuild": "^0.19.3", - "esbuild-plugin-copy-directories": "^1.0.0", - "esbuild-plugin-time": "^1.0.0", - "esbuild-plugin-yaml": "^0.0.1", - "eslint": "^8.49.0", - "eslint-plugin-jsdoc": "^46.8.1", - "globals": "^13.20.0", - "portfinder": "^1.0.32", - "tsx": "^4.7.1", - "typescript": "~5.2.2" - }, - "optionalDependencies": { - "@esbuild/darwin-x64": "^0.17.15", - "@esbuild/linux-x64": "^0.17.15", - "@esbuild/windows-x64": "^0.17.15" - } -} diff --git a/app/ide-desktop/lib/content/src/devServiceWorker.ts b/app/ide-desktop/lib/content/src/devServiceWorker.ts deleted file mode 100644 index ac46191942..0000000000 --- a/app/ide-desktop/lib/content/src/devServiceWorker.ts +++ /dev/null @@ -1,64 +0,0 @@ -/** @file A service worker that redirects paths without extensions to `/index.html`. - * This is required for paths like `/login`, which are handled by client-side routing, - * to work when developing locally on `localhost:8080`. */ -// Bring globals and interfaces specific to Web Workers into scope. -/// -import * as common from 'enso-common' - -import * as constants from './serviceWorkerConstants' - -// ===================== -// === Fetch handler === -// ===================== - -// We `declare` a variable here because Service Workers have a different global scope. -// eslint-disable-next-line no-restricted-syntax -declare const self: ServiceWorkerGlobalScope - -self.addEventListener('install', event => { - event.waitUntil( - caches.open(constants.CACHE_NAME).then(cache => { - void cache.addAll(constants.DEPENDENCIES) - return - }) - ) -}) - -self.addEventListener('fetch', event => { - const url = new URL(event.request.url) - if ( - (url.hostname === 'localhost' || url.hostname === '127.0.0.1') && - url.pathname === '/esbuild' - ) { - return false - } else if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') { - const responsePromise = caches - .open(constants.CACHE_NAME) - .then(cache => cache.match(event.request)) - .then( - response => - response ?? - (/\/[^.]+$/.test(url.pathname) - ? fetch('/index.html') - : fetch(event.request.url)) - ) - event.respondWith( - responsePromise.then(response => { - const clonedResponse = new Response(response.body, response) - for (const [header, value] of common.COOP_COEP_CORP_HEADERS) { - clonedResponse.headers.set(header, value) - } - return clonedResponse - }) - ) - return - } else { - event.respondWith( - caches - .open(constants.CACHE_NAME) - .then(cache => cache.match(event.request)) - .then(response => response ?? fetch(event.request)) - ) - return - } -}) diff --git a/app/ide-desktop/lib/content/src/entrypoint.ts b/app/ide-desktop/lib/content/src/entrypoint.ts deleted file mode 100644 index f6c04b7ff6..0000000000 --- a/app/ide-desktop/lib/content/src/entrypoint.ts +++ /dev/null @@ -1,348 +0,0 @@ -/** @file This module is responsible for loading the WASM binary, its dependencies, and providing - * the user with a visual representation of this process (welcome screen). It also implements a view - * allowing to choose a debug rendering test from. */ - -import * as semver from 'semver' -import * as toastify from 'react-toastify' - -import * as app from 'enso-runner/src/runner' -import * as common from 'enso-common' -import * as contentConfig from 'enso-content-config' -import * as dashboard from 'enso-dashboard' -import * as detect from 'enso-common/src/detect' -import * as gtag from 'enso-common/src/gtag' - -import * as remoteLog from './remoteLog' -import GLOBAL_CONFIG from '../../../../gui2/config.yaml' assert { type: 'yaml' } - -const logger = app.log.logger - -// ================= -// === Constants === -// ================= - -/** The name of the `localStorage` key storing the initial URL of the app. */ -const INITIAL_URL_KEY = `${common.PRODUCT_NAME.toLowerCase()}-initial-url` -/** Path to the SSE endpoint over which esbuild sends events. */ -const ESBUILD_PATH = './esbuild' -/** SSE event indicating a build has finished. */ -const ESBUILD_EVENT_NAME = 'change' -/** Path to the serice worker that caches assets for offline usage. - * In development, it also resolves all extensionless paths to `/index.html`. - * This is required for client-side routing to work when doing `./run gui watch`. - */ -const SERVICE_WORKER_PATH = './serviceWorker.js' -/** One second in milliseconds. */ -const SECOND = 1000 -/** Time in seconds after which a `fetchTimeout` ends. */ -const FETCH_TIMEOUT = 300 - -// =================== -// === Live reload === -// =================== - -if (detect.IS_DEV_MODE && !detect.isOnElectron()) { - new EventSource(ESBUILD_PATH).addEventListener(ESBUILD_EVENT_NAME, () => { - // This acts like `location.reload`, but it preserves the query-string. - // The `toString()` is to bypass a lint without using a comment. - location.href = location.href.toString() - }) -} -void (async () => { - // `navigator.serviceWorker` may be disabled in certain situations, for example in Private mode - // on Safari. - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - const registration = await navigator.serviceWorker?.getRegistration() - await registration?.unregister() - await navigator.serviceWorker.register(SERVICE_WORKER_PATH) -})() - -// ============= -// === Fetch === -// ============= - -/** Returns an `AbortController` that aborts after the specified number of seconds. */ -function timeout(timeSeconds: number) { - const controller = new AbortController() - setTimeout(() => { - controller.abort() - }, timeSeconds * SECOND) - return controller -} - -/** A version of `fetch` which times out after the provided time. */ -async function fetchTimeout(url: string, timeoutSeconds: number): Promise { - return fetch(url, { signal: timeout(timeoutSeconds).signal }).then(response => { - const statusCodeOK = 200 - if (response.status === statusCodeOK) { - return response.json() - } else { - throw new Error(`Failed to fetch '${url}'. Response status: ${response.status}.`) - } - }) -} - -/** Return `true` if the current application version is still supported and `false` otherwise. - * - * Function downloads the application config containing the minimum supported version from GitHub - * and compares it with the version of the `client` js package. When the function is unable to - * download the application config, or one of the compared versions does not match the semver - * scheme, it returns `true`. */ -async function checkMinSupportedVersion(config: typeof contentConfig.OPTIONS) { - let supported = false - if (config.groups.engine.options.skipMinVersionCheck.value) { - supported = true - } else { - try { - const appConfig = await fetchTimeout( - config.groups.engine.options.configUrl.value, - FETCH_TIMEOUT - ) - if ( - typeof appConfig === 'object' && - appConfig != null && - 'minimumSupportedVersion' in appConfig - ) { - const minSupportedVersion = appConfig.minimumSupportedVersion - if (typeof minSupportedVersion !== 'string') { - logger.error('The minimum supported version is not a string.') - } else { - const comparator = new semver.Comparator(`>=${minSupportedVersion}`) - supported = comparator.test(contentConfig.VERSION.ide) - } - } else { - logger.error('The application config is not an object.') - } - } catch (e) { - console.error('Minimum version check failed.', e) - supported = true - } - } - return supported -} - -/** Display information that the current app version is deprecated. */ -function displayDeprecatedVersionDialog() { - const versionCheckText = document.createTextNode( - 'This version is no longer supported. Please download a new one.' - ) - - const root = document.getElementById('root') - const versionCheckDiv = document.createElement('div') - versionCheckDiv.id = 'version-check' - versionCheckDiv.className = 'auth-info' - versionCheckDiv.style.display = 'block' - versionCheckDiv.appendChild(versionCheckText) - if (root == null) { - console.error('Cannot find the root DOM element.') - } else { - root.appendChild(versionCheckDiv) - } -} - -// ======================== -// === Main entry point === -// ======================== - -/** Nested configuration options with `string` values. */ -export interface StringConfig { - [key: string]: StringConfig | string -} - -/** Configuration options for the authentication flow and dashboard. */ -interface AuthenticationConfig { - readonly supportsVibrancy: boolean - readonly projectManagerUrl: string | null - readonly isInAuthenticationFlow: boolean - readonly shouldUseAuthentication: boolean - readonly initialProjectName: string | null -} - -/** Contains the entrypoint into the IDE. */ -class Main implements AppRunner { - app: app.App | null = null - toast = toastify.toast - - /** Stop an app instance, if one is running. */ - stopApp() { - this.app?.stop() - } - - /** Run an app instance with the specified configuration. - * This includes the scene to run and the WebSocket endpoints to the backend. */ - async runApp( - inputConfig: StringConfig | null, - accessToken: string | null, - loggingMetadata?: object - ) { - this.stopApp() - - /** FIXME: https://github.com/enso-org/enso/issues/6475 - * Default values names are out of sync with values used in code. - * Rather than setting fixed values here we need to fix default values in config. */ - const config = Object.assign( - { - loader: { - wasmUrl: 'pkg-opt.wasm', - }, - }, - inputConfig - ) - - const configOptions = contentConfig.OPTIONS.clone() - - const newApp = new app.App({ - config, - configOptions, - packageInfo: { - version: BUILD_INFO.version, - engineVersion: BUILD_INFO.engineVersion, - }, - }) - - // We override the remote logger stub with the "real" one. Eventually the runner should not - // be aware of the remote logger at all, and it should be integrated with our logging infrastructure. - const remoteLogger = accessToken != null ? new remoteLog.RemoteLogger(accessToken) : null - newApp.remoteLog = (message: string, metadata: unknown) => { - const metadataObject = - typeof metadata === 'object' && metadata != null ? metadata : { metadata } - const actualMetadata = - loggingMetadata == null ? metadata : { ...loggingMetadata, ...metadataObject } - if (newApp.config.options.dataCollection.value && remoteLogger != null) { - // FIXME [sb]: https://github.com/enso-org/cloud-v2/issues/735 - // The current GUI sends a lot of logs (over 300) every time a project is opened. - // This severely degrades performance, and the logs generated do not appear to be - // useful. This should be re-enabled when the GUI no longer sends a large amount - // of logs. - // await remoteLogger.remoteLog(message, actualMetadata) - } else { - const logMessage = [ - 'Not sending log to remote server. Data collection is disabled.', - `Message: "${message}"`, - `Metadata: ${JSON.stringify(actualMetadata)}`, - ].join(' ') - - logger.log(logMessage) - } - return Promise.resolve() - } - this.app = newApp - - if (!this.app.initialized) { - console.error('Failed to initialize the application.') - } else { - if (!(await checkMinSupportedVersion(configOptions))) { - displayDeprecatedVersionDialog() - } else { - const email = configOptions.groups.authentication.options.email.value - // The default value is `""`, so a truthiness check is most appropriate here. - if (email) { - logger.log(`User identified as '${email}'.`) - } - void this.app.run() - } - } - } - - /** The entrypoint into the IDE. */ - main(inputConfig?: StringConfig) { - /** Note: Signing out always redirects to `/`. It is impossible to make this work, - * as it is not possible to distinguish between having just logged out, and explicitly - * opening a page with no URL parameters set. - * - * Client-side routing endpoints are explicitly not supported for live-reload, as they are - * transitional pages that should not need live-reload when running `gui watch`. */ - const url = new URL(location.href) - const isInAuthenticationFlow = url.searchParams.has('code') && url.searchParams.has('state') - const authenticationUrl = location.href - if (isInAuthenticationFlow) { - gtag.gtag('event', 'cloud_sign_in_redirect') - history.replaceState(null, '', localStorage.getItem(INITIAL_URL_KEY)) - } - const configOptions = contentConfig.OPTIONS.clone() - const parseOk = configOptions.loadAllAndDisplayHelpIfUnsuccessful([app.urlParams()]) - if (isInAuthenticationFlow) { - history.replaceState(null, '', authenticationUrl) - } else { - localStorage.setItem(INITIAL_URL_KEY, location.href) - } - if (parseOk) { - const supportsVibrancy = configOptions.groups.window.options.vibrancy.value - const shouldUseAuthentication = configOptions.options.authentication.value - const isOpeningMainEntryPoint = - configOptions.groups.startup.options.entry.value === - configOptions.groups.startup.options.entry.default - const initialProjectName = configOptions.groups.startup.options.project.value || null - // This does not need to be removed from the URL, but only because local projects - // also use the Project Manager URL, and remote (cloud) projects remove the URL - // completely. - const projectManagerUrl = - configOptions.groups.engine.options.projectManagerUrl.value || null - // This MUST be removed as it would otherwise override the `startup.project` passed - // explicitly in `ide.tsx`. - if (isOpeningMainEntryPoint && url.searchParams.has('startup.project')) { - url.searchParams.delete('startup.project') - history.replaceState(null, '', url.toString()) - } - if (shouldUseAuthentication && isOpeningMainEntryPoint) { - this.runAuthentication({ - supportsVibrancy, - isInAuthenticationFlow, - projectManagerUrl, - shouldUseAuthentication, - initialProjectName, - }) - } else { - void this.runApp(inputConfig ?? null, null) - } - } - } - - /** Begins the authentication UI flow. */ - runAuthentication(config: AuthenticationConfig) { - const ideElement = document.getElementById('root') - if (ideElement) { - ideElement.style.top = '-100vh' - ideElement.style.position = 'fixed' - } - const ide2Element = document.getElementById('app') - if (ide2Element) { - ide2Element.style.display = 'none' - } - - /** TODO [NP]: https://github.com/enso-org/cloud-v2/issues/345 - * `content` and `dashboard` packages **MUST BE MERGED INTO ONE**. The IDE - * should only have one entry point. Right now, we have two. One for the cloud - * and one for the desktop. */ - /** FIXME [PB]: https://github.com/enso-org/cloud-v2/issues/366 - * React hooks rerender themselves multiple times. It is resulting in multiple - * Enso main scene being initialized. As a temporary workaround we check whether - * appInstance was already ran. Target solution should move running appInstance - * where it will be called only once. */ - dashboard.run({ - appRunner: this, - logger, - vibrancy: config.supportsVibrancy, - supportsLocalBackend: SUPPORTS_LOCAL_BACKEND, - supportsDeepLinks: SUPPORTS_DEEP_LINKS, - projectManagerUrl: config.projectManagerUrl, - isAuthenticationDisabled: !config.shouldUseAuthentication, - shouldShowDashboard: true, - initialProjectName: config.initialProjectName, - onAuthenticated: () => { - if (config.isInAuthenticationFlow) { - const initialUrl = localStorage.getItem(INITIAL_URL_KEY) - if (initialUrl != null) { - // This is not used past this point, however it is set to the initial URL - // to make refreshing work as expected. - history.replaceState(null, '', initialUrl) - } - } - }, - }) - } -} - -// @ts-expect-error `globalConfig.windowAppScopeName` is not known at typecheck time. -// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -window[GLOBAL_CONFIG.windowAppScopeName] = new Main() diff --git a/app/ide-desktop/lib/content/src/index.html b/app/ide-desktop/lib/content/src/index.html deleted file mode 100644 index 08121c25e7..0000000000 --- a/app/ide-desktop/lib/content/src/index.html +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - Enso - - - - - - - - -
-
-
- - - - - - diff --git a/app/ide-desktop/lib/content/src/remoteLog.ts b/app/ide-desktop/lib/content/src/remoteLog.ts deleted file mode 100644 index 84c0abefbb..0000000000 --- a/app/ide-desktop/lib/content/src/remoteLog.ts +++ /dev/null @@ -1,77 +0,0 @@ -/** @file Defines the {@link RemoteLogger} class and {@link remoteLog} function for sending logs to a remote server. - * {@link RemoteLogger} provides a convenient way to manage remote logging with access token authorization. */ - -import * as app from 'enso-runner/src/runner' - -const logger = app.log.logger - -// ================= -// === Constants === -// ================= - -/** URL address where remote logs should be sent. */ -const REMOTE_LOG_URL = - process.env.ENSO_CLOUD_API_URL == null - ? null - : new URL(`${process.env.ENSO_CLOUD_API_URL}/logs`) - -// ==================== -// === RemoteLogger === -// ==================== - -// === Class === - -/** Helper class facilitating sending logs to a remote. */ -export class RemoteLogger { - /** Initialize a new instance. - * @param accessToken - JWT token used to authenticate within the cloud. */ - constructor(public accessToken: string) { - this.accessToken = accessToken - } - - /** Sends a log message to a remote. - * @param message - The log message to send. - * @param metadata - Additional metadata to send along with the log. - * @returns Promise which resolves when the log message has been sent. */ - async remoteLog(message: string, metadata: unknown): Promise { - await remoteLog(this.accessToken, message, metadata) - } -} - -// === Underlying logic === - -/** Sends a log message to a remote server using the provided access token. - * @param accessToken - The access token for authentication. - * @param message - The message to be logged on the server. - * @param metadata - Additional metadata to include in the log. - * @throws Will throw an error if the response from the server is not okay (response status is not 200). - * @returns Returns a promise that resolves when the log message is successfully sent. */ -export async function remoteLog( - accessToken: string, - message: string, - metadata: unknown -): Promise { - if (REMOTE_LOG_URL != null) { - try { - const headers: HeadersInit = [ - ['Content-Type', 'application/json'], - ['Authorization', `Bearer ${accessToken}`], - ] - const body = JSON.stringify({ message, metadata }) - const response = await fetch(REMOTE_LOG_URL, { method: 'POST', headers, body }) - if (response.ok) { - return - } else { - const errorMessage = `Error while sending log to a remote: Status ${response.status}.` - const text = await response.text().catch(error => { - throw new Error(`${errorMessage} Failed to read response: ${String(error)}.`) - }) - throw new Error(`${errorMessage} Response: ${text}.`) - } - } catch (error) { - logger.error(error) - // eslint-disable-next-line no-restricted-syntax - throw error - } - } -} diff --git a/app/ide-desktop/lib/content/src/run.js b/app/ide-desktop/lib/content/src/run.js deleted file mode 100644 index 992d79c059..0000000000 --- a/app/ide-desktop/lib/content/src/run.js +++ /dev/null @@ -1,10 +0,0 @@ -/** @file This file is used to simply run the IDE. It can be not invoked if the IDE needs to be used - * as a library. */ - -// =============== -// === Run IDE === -// =============== - -// This `void` is used to explicitly not `await` a promise, not to produce an `undefined`. -// eslint-disable-next-line no-restricted-syntax -void window.enso?.main() diff --git a/app/ide-desktop/lib/content/src/serviceWorker.ts b/app/ide-desktop/lib/content/src/serviceWorker.ts deleted file mode 100644 index 1e7ac272e9..0000000000 --- a/app/ide-desktop/lib/content/src/serviceWorker.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** @file A service worker that redirects paths without extensions to `/index.html`. - * This is required for paths like `/login`, which are handled by client-side routing, - * to work when developing locally on `localhost:8080`. */ -// Bring globals and interfaces specific to Web Workers into scope. -/// -import * as constants from './serviceWorkerConstants' - -// ===================== -// === Fetch handler === -// ===================== - -// We `declare` a variable here because Service Workers have a different global scope. -// eslint-disable-next-line no-restricted-syntax -declare const self: ServiceWorkerGlobalScope - -self.addEventListener('install', event => { - event.waitUntil( - caches.open(constants.CACHE_NAME).then(cache => { - void cache.addAll(constants.DEPENDENCIES) - return - }) - ) -}) - -self.addEventListener('fetch', event => { - const url = new URL(event.request.url) - if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') { - return false - } else { - event.respondWith( - caches - .open(constants.CACHE_NAME) - .then(cache => cache.match(event.request)) - .then(response => response ?? fetch(event.request)) - ) - return - } -}) diff --git a/app/ide-desktop/lib/content/src/serviceWorkerConstants.js b/app/ide-desktop/lib/content/src/serviceWorkerConstants.js deleted file mode 100644 index 726736d780..0000000000 --- a/app/ide-desktop/lib/content/src/serviceWorkerConstants.js +++ /dev/null @@ -1,59 +0,0 @@ -/** @file Constants shared between all service workers (development and production). */ -import * as common from 'enso-common' - -// ================= -// === Constants === -// ================= - -/** The name of the cache under which offline assets are stored. */ -export const CACHE_NAME = common.PRODUCT_NAME.toLowerCase() - -/** The numbers after each font loaded by the "M PLUS 1" font. */ -const M_PLUS_1_SECTIONS = [ - /* eslint-disable @typescript-eslint/no-magic-numbers */ - 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, - 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 53, - 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, - 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, - 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, - /* eslint-enable @typescript-eslint/no-magic-numbers */ -] - -/** The complete list of assets to cache for offline use. */ -export const DEPENDENCIES = [ - // app/gui/view/graph-editor/src/builtin/visualization/java_script/heatmap.js - // app/gui/view/graph-editor/src/builtin/visualization/java_script/histogram.js - // app/gui/view/graph-editor/src/builtin/visualization/java_script/scatterPlot.js - 'https://d3js.org/d3.v4.min.js', - 'https://fonts.cdnfonts.com/css/dejavu-sans-mono', - // Loaded by https://fonts.cdnfonts.com/css/dejavu-sans-mono - 'https://fonts.cdnfonts.com/s/108/DejaVuSansMono.woff', - 'https://fonts.cdnfonts.com/s/108/DejaVuSansMono-Oblique.woff', - 'https://fonts.cdnfonts.com/s/108/DejaVuSansMono-Bold.woff', - 'https://fonts.cdnfonts.com/s/108/DejaVuSansMono-BoldOblique.woff', - // app/gui/view/graph-editor/src/builtin/visualization/java_script/geoMap.js - 'https://unpkg.com/deck.gl@8.4/dist.min.js', - 'https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.js', - 'https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.css', - // Loaded by https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.js - 'https://api.mapbox.com/styles/v1/mapbox/light-v9?access_token=pk.' + - 'eyJ1IjoiZW5zby1vcmciLCJhIjoiY2tmNnh5MXh2MGlyOTJ5cWdubnFxbXo4ZSJ9.3KdAcCiiXJcSM18nwk09-Q', - 'https://api.mapbox.com/styles/v1/mapbox/light-v9/sprite.json?access_token=pk.' + - 'eyJ1IjoiZW5zby1vcmciLCJhIjoiY2tmNnh5MXh2MGlyOTJ5cWdubnFxbXo4ZSJ9.3KdAcCiiXJcSM18nwk09-Q', - 'https://api.mapbox.com/styles/v1/mapbox/light-v9/sprite.png?access_token=pk.' + - 'eyJ1IjoiZW5zby1vcmciLCJhIjoiY2tmNnh5MXh2MGlyOTJ5cWdubnFxbXo4ZSJ9.3KdAcCiiXJcSM18nwk09-Q', - // app/gui/view/graph-editor/src/builtin/visualization/java_script/sql.js - 'https://cdnjs.cloudflare.com/ajax/libs/sql-formatter/4.0.2/sql-formatter.min.js', - // app/gui/view/graph-editor/src/builtin/visualization/java_script/table.js - 'https://cdn.jsdelivr.net/npm/ag-grid-community/dist/ag-grid-community.min.js', - 'https://cdn.jsdelivr.net/npm/ag-grid-community/styles/ag-grid.css', - 'https://cdn.jsdelivr.net/npm/ag-grid-community/styles/ag-theme-alpine.css', - // app/ide-desktop/lib/dashboard/src/tailwind.css - 'https://fonts.googleapis.com/css2?family=M+PLUS+1:wght@500;700&display=swap', - // Loaded by https://fonts.googleapis.com/css2?family=M+PLUS+1:wght@500;700&display=swap - ...M_PLUS_1_SECTIONS.map( - number => - 'https://fonts.gstatic.com/s/mplus1/v6/' + - `R70ZjygA28ymD4HgBVu92j6eR1mYP_TX-Bb-rTg93gHfHe9F4Q.${number}.woff2` - ), -] diff --git a/app/ide-desktop/lib/content/src/style.css b/app/ide-desktop/lib/content/src/style.css deleted file mode 100644 index 86c3ca8c1f..0000000000 --- a/app/ide-desktop/lib/content/src/style.css +++ /dev/null @@ -1,262 +0,0 @@ -/* Fonts */ - -@font-face { - font-family: "M PLUS 1"; - src: url("/MPLUS1[wght].ttf") format("truetype"); - font-weight: 100; - font-display: block; -} - -@font-face { - font-family: "M PLUS 1"; - src: url("/MPLUS1[wght].ttf") format("truetype"); - font-style: normal; - font-weight: 200; - font-display: block; -} - -@font-face { - font-family: "M PLUS 1"; - src: url("/MPLUS1[wght].ttf") format("truetype"); - font-style: normal; - font-weight: 300; - font-display: block; -} - -@font-face { - font-family: "M PLUS 1"; - src: url("/MPLUS1[wght].ttf") format("truetype"); - font-style: normal; - font-weight: 400; - font-display: block; -} - -@font-face { - font-family: "M PLUS 1"; - src: url("/MPLUS1[wght].ttf") format("truetype"); - font-style: normal; - font-weight: 500; - font-display: block; -} - -@font-face { - font-family: "M PLUS 1"; - src: url("/MPLUS1[wght].ttf") format("truetype"); - font-style: normal; - font-weight: 600; - font-display: block; -} - -@font-face { - font-family: "M PLUS 1"; - src: url("/MPLUS1[wght].ttf") format("truetype"); - font-style: normal; - font-weight: 700; - font-display: block; -} - -@font-face { - font-family: "M PLUS 1"; - src: url("/MPLUS1[wght].ttf") format("truetype"); - font-style: normal; - font-weight: 800; - font-display: block; -} - -@font-face { - font-family: "M PLUS 1"; - src: url("/MPLUS1[wght].ttf") format("truetype"); - font-style: normal; - font-weight: 900; - font-display: block; -} - -/* End of fonts */ - -html, -body { - height: 100vh; -} - -body { - margin: 0; - overscroll-behavior: none; -} - -#root { - height: 100vh; - width: 100vw; - margin: 0; - position: absolute; - overflow: hidden; -} - -.visualization { - z-index: 2; - border-radius: 14px; -} - -.auth-header { - font-family: sans-serif; - text-align: center; - margin: 24px auto; -} - -.auth-text { - text-align: justify; - font-family: sans-serif; - color: #454545; - width: 50%; - margin: 12px auto; -} - -.auth-info { - text-align: center; - font-family: sans-serif; - color: #454545; - margin: 24px auto; - display: none; -} - -#crash-banner { - background: DarkSalmon; - color: #2c1007; - - font-family: sans-serif; - line-height: 1.5; - - position: absolute; - - /* Put the banner in front of the "root" node which has index 1 */ - z-index: 2; - - /* Center the banner horizontally */ - left: 0; - right: 0; - margin: auto; - - width: fit-content; - padding: 1em; - - border-bottom-left-radius: 8px; - border-bottom-right-radius: 8px; -} - -#crash-banner button { - border-radius: 4px; - border: none; - font: inherit; - - /* Balance padding with negative margin to make the label fit with other text */ - padding: 2px; - margin: -2px; - - padding-left: 0.5em; - padding-right: 0.5em; -} -#crash-banner button:focus { - /* Show a 2px outline, following the button's shape, instead of the standard - rectangular outline */ - outline: none; - box-shadow: 0 0 0 2px #fbeee9; -} - -#crash-banner #crash-banner-close-button { - float: right; - margin-left: 0.75em; - - color: #2c1007; - background: none; -} -#crash-banner #crash-banner-close-button:hover { - color: #58210e; -} -#crash-banner #crash-banner-close-button:active { - color: #843115; -} - -#crash-banner #crash-report-button { - float: right; - margin-left: 1em; - - color: DarkSalmon; - background: #2c1007; -} -#crash-banner #crash-report-button:hover { - background-color: #58210e; -} -#crash-banner #crash-report-button:active { - background-color: #843115; -} - -#crash-banner-content { - display: inline; -} - -#crash-banner hr { - height: 1px; - border: none; - background: #b96a50; - margin: 0.8em -1em; -} - -#debug-root { - width: 100vw; - height: 100vh; - position: absolute; - top: 0; - left: 0; - margin: 0; - overflow: hidden; - pointer-events: none; - display: none; -} - -#debug-enable-checkbox { - position: absolute; - bottom: 0px; - right: 0px; - color: white; - font-size: 12px; - font-family: "M PLUS 1"; - padding: 3px 6px; - background: rgba(15, 0, 77, 0.7); - cursor: none; - user-select: none; -} - -#debug-enable-checkbox:has(input:checked) + #debug-root { - display: initial; -} - -#debug-root > .debug-layer { - position: absolute; - top: 50vh; - left: 50vw; - width: 0px; - height: 0px; - transform-origin: 0 0; -} - -#debug-root > .debug-layer * { - position: absolute; - background: rgba(0, 0, 0, 0.05); - box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); - transform-origin: 0 0; -} - -#debug-root > .debug-layer [data-name="Text"] > div { - background: rgba(0, 160, 60, 0.15); -} - -#debug-root > .debug-layer [data-name*="compound::rectangle::shape"] { - background: rgba(0, 20, 180, 0.15); -} - -#debug-root > .debug-layer .hidden { - display: none; -} - -#debug-root > .debug-layer[data-layer-name="DETACHED"] { - display: none; -} diff --git a/app/ide-desktop/lib/content/src/wasm_imports.js b/app/ide-desktop/lib/content/src/wasm_imports.js deleted file mode 100644 index 6c6faa4f5a..0000000000 --- a/app/ide-desktop/lib/content/src/wasm_imports.js +++ /dev/null @@ -1,11 +0,0 @@ -/** @file This module exports wasm Rust glue code generated by EnsoGL Pack. */ - -import * as wasmRustGlue from 'wasm_rust_glue' - -// ============================= -// === Export WASM Rust glue === -// ============================= - -// Eslint is not (and should not be) set up to check CommonJS. -// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -exports.init = wasmRustGlue.default diff --git a/app/ide-desktop/lib/content/start.ts b/app/ide-desktop/lib/content/start.ts deleted file mode 100644 index 3ccdfd4b73..0000000000 --- a/app/ide-desktop/lib/content/start.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** @file Start the file watch service. */ -import * as esbuild from 'esbuild' -import * as portfinder from 'portfinder' - -import * as bundler from './esbuild-config.js' - -// ================= -// === Constants === -// ================= - -const PORT = 8080 -const HTTP_STATUS_OK = 200 - -// =============== -// === Watcher === -// =============== - -/** Start the esbuild watcher. */ -async function watch() { - const options = bundler.bundleOptions({ supportsLocalBackend: true, supportsDeepLinks: false }) - const builder = await esbuild.context(options) - await builder.watch() - await builder.serve({ - port: await portfinder.getPortPromise({ port: PORT }), - servedir: options.outdir, - /** This function is called on every request. - * It is used here to show an error if the file to serve was not found. */ - onRequest(args) { - if (args.status !== HTTP_STATUS_OK) { - console.error(`HTTP error ${args.status} when serving path '${args.path}'.`) - } - }, - }) -} - -void watch() diff --git a/app/ide-desktop/lib/content/tsconfig.json b/app/ide-desktop/lib/content/tsconfig.json deleted file mode 100644 index 33738ff46d..0000000000 --- a/app/ide-desktop/lib/content/tsconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "include": ["../types", "../../../../build.json", "."], - "references": [{ "path": "../dashboard" }] -} diff --git a/app/ide-desktop/lib/content/watch.ts b/app/ide-desktop/lib/content/watch.ts deleted file mode 100644 index 053b5c341f..0000000000 --- a/app/ide-desktop/lib/content/watch.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** @file File watch and compile service. */ -import * as path from 'node:path' -import * as url from 'node:url' - -import * as esbuild from 'esbuild' -import * as portfinder from 'portfinder' -import chalk from 'chalk' - -import * as bundler from './esbuild-config' -import * as dashboardBundler from '../dashboard/esbuild-config' - -// ================= -// === Constants === -// ================= - -/** The path of this file. */ -const THIS_PATH = path.resolve(path.dirname(url.fileURLToPath(import.meta.url))) -const PORT = 8080 -const HTTP_STATUS_OK = 200 - -// =============== -// === Watcher === -// =============== - -/** Starts the esbuild watcher. */ -async function watch() { - const dashboardOpts = dashboardBundler.bundleOptions() - const dashboardBuilder = await esbuild.context(dashboardOpts) - // 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. - await dashboardBuilder.watch() - const args = bundler.argumentsFromEnv({ supportsLocalBackend: true, supportsDeepLinks: false }) - const options = bundler.bundlerOptions(args) - options.pure.splice(options.pure.indexOf('assert'), 1) - options.define.REDIRECT_OVERRIDE = JSON.stringify('http://localhost:8080') - // This is safe as this entry point is statically known. - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const serviceWorkerEntryPoint = options.entryPoints.find( - entryPoint => entryPoint.out === 'serviceWorker' - )! - serviceWorkerEntryPoint.in = path.resolve(THIS_PATH, 'src', 'devServiceWorker.ts') - const builder = await esbuild.context(options) - await builder.watch() - await builder.serve({ - port: await portfinder.getPortPromise({ port: PORT }), - servedir: options.outdir, - /** This function is called on every request. - * It is used here to show an error if the file to serve was not found. */ - onRequest(request) { - if (request.status !== HTTP_STATUS_OK) { - console.error( - chalk.red(`HTTP error ${request.status} when serving path '${request.path}'.`) - ) - } - }, - }) -} - -void watch() diff --git a/app/ide-desktop/lib/dashboard/package.json b/app/ide-desktop/lib/dashboard/package.json index 33ee172eb9..56e9978b13 100644 --- a/app/ide-desktop/lib/dashboard/package.json +++ b/app/ide-desktop/lib/dashboard/package.json @@ -66,6 +66,7 @@ "esbuild": "^0.19.3", "esbuild-plugin-inline-image": "^0.0.9", "esbuild-plugin-time": "^1.0.0", + "esbuild-plugin-yaml": "^0.0.1", "eslint": "^8.49.0", "eslint-plugin-jsdoc": "^46.8.1", "eslint-plugin-react": "^7.32.1", diff --git a/app/ide-desktop/lib/icons/package.json b/app/ide-desktop/lib/icons/package.json index 03eb7383c9..e9cc8b7fb2 100644 --- a/app/ide-desktop/lib/icons/package.json +++ b/app/ide-desktop/lib/icons/package.json @@ -18,7 +18,9 @@ }, "devDependencies": { "sharp": "^0.31.2", - "to-ico": "^1.1.5" + "to-ico": "^1.1.5", + "@types/sharp": "^0.31.1", + "@types/to-ico": "^1.1.1" }, "type": "module" } diff --git a/build/build/Cargo.toml b/build/build/Cargo.toml index be6599f884..acbdfacd18 100644 --- a/build/build/Cargo.toml +++ b/build/build/Cargo.toml @@ -25,7 +25,6 @@ heck = "0.4.0" enso-build-base = { path = "../base" } enso-enso-font = { path = "../../lib/rust/enso-font" } enso-font = { path = "../../lib/rust/font" } -enso-pack = { path = "../../lib/rust/enso-pack" } ide-ci = { path = "../ci_utils" } mime = "0.3.16" new_mime_guess = "4.0.1" diff --git a/lib/rust/enso-pack/js/.eslintrc.cjs b/lib/js/runner/.eslintrc.cjs similarity index 100% rename from lib/rust/enso-pack/js/.eslintrc.cjs rename to lib/js/runner/.eslintrc.cjs diff --git a/lib/rust/enso-pack/js/package.json b/lib/js/runner/package.json similarity index 100% rename from lib/rust/enso-pack/js/package.json rename to lib/js/runner/package.json diff --git a/lib/rust/enso-pack/js/src/runner/animation.ts b/lib/js/runner/src/runner/animation.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/animation.ts rename to lib/js/runner/src/runner/animation.ts diff --git a/lib/rust/enso-pack/js/src/runner/config.json b/lib/js/runner/src/runner/config.json similarity index 100% rename from lib/rust/enso-pack/js/src/runner/config.json rename to lib/js/runner/src/runner/config.json diff --git a/lib/rust/enso-pack/js/src/runner/config.ts b/lib/js/runner/src/runner/config.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/config.ts rename to lib/js/runner/src/runner/config.ts diff --git a/lib/rust/enso-pack/js/src/runner/data/array.ts b/lib/js/runner/src/runner/data/array.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/data/array.ts rename to lib/js/runner/src/runner/data/array.ts diff --git a/lib/rust/enso-pack/js/src/runner/debug/help-screen.ts b/lib/js/runner/src/runner/debug/help-screen.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/debug/help-screen.ts rename to lib/js/runner/src/runner/debug/help-screen.ts diff --git a/lib/rust/enso-pack/js/src/runner/debug/index.ts b/lib/js/runner/src/runner/debug/index.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/debug/index.ts rename to lib/js/runner/src/runner/debug/index.ts diff --git a/lib/rust/enso-pack/js/src/runner/debug/package-info.ts b/lib/js/runner/src/runner/debug/package-info.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/debug/package-info.ts rename to lib/js/runner/src/runner/debug/package-info.ts diff --git a/lib/rust/enso-pack/js/src/runner/dom/dom.ts b/lib/js/runner/src/runner/dom/dom.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/dom/dom.ts rename to lib/js/runner/src/runner/dom/dom.ts diff --git a/lib/rust/enso-pack/js/src/runner/dom/logo.ts b/lib/js/runner/src/runner/dom/logo.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/dom/logo.ts rename to lib/js/runner/src/runner/dom/logo.ts diff --git a/lib/rust/enso-pack/js/src/runner/dom/svg.ts b/lib/js/runner/src/runner/dom/svg.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/dom/svg.ts rename to lib/js/runner/src/runner/dom/svg.ts diff --git a/lib/rust/enso-pack/js/src/runner/host.ts b/lib/js/runner/src/runner/host.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/host.ts rename to lib/js/runner/src/runner/host.ts diff --git a/lib/rust/enso-pack/js/src/runner/index.ts b/lib/js/runner/src/runner/index.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/index.ts rename to lib/js/runner/src/runner/index.ts diff --git a/lib/rust/enso-pack/js/src/runner/log/index.ts b/lib/js/runner/src/runner/log/index.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/log/index.ts rename to lib/js/runner/src/runner/log/index.ts diff --git a/lib/rust/enso-pack/js/src/runner/log/logger.ts b/lib/js/runner/src/runner/log/logger.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/log/logger.ts rename to lib/js/runner/src/runner/log/logger.ts diff --git a/lib/rust/enso-pack/js/src/runner/log/router.ts b/lib/js/runner/src/runner/log/router.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/log/router.ts rename to lib/js/runner/src/runner/log/router.ts diff --git a/lib/rust/enso-pack/js/src/runner/math.ts b/lib/js/runner/src/runner/math.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/math.ts rename to lib/js/runner/src/runner/math.ts diff --git a/lib/rust/enso-pack/js/src/runner/name.ts b/lib/js/runner/src/runner/name.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/name.ts rename to lib/js/runner/src/runner/name.ts diff --git a/lib/rust/enso-pack/js/src/runner/wasm/entry-point.ts b/lib/js/runner/src/runner/wasm/entry-point.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/wasm/entry-point.ts rename to lib/js/runner/src/runner/wasm/entry-point.ts diff --git a/lib/rust/enso-pack/js/src/runner/wasm/index.ts b/lib/js/runner/src/runner/wasm/index.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/wasm/index.ts rename to lib/js/runner/src/runner/wasm/index.ts diff --git a/lib/rust/enso-pack/js/src/runner/wasm/loader.ts b/lib/js/runner/src/runner/wasm/loader.ts similarity index 100% rename from lib/rust/enso-pack/js/src/runner/wasm/loader.ts rename to lib/js/runner/src/runner/wasm/loader.ts diff --git a/lib/rust/enso-pack/js/tsconfig.json b/lib/js/runner/tsconfig.json similarity index 100% rename from lib/rust/enso-pack/js/tsconfig.json rename to lib/js/runner/tsconfig.json diff --git a/lib/rust/enso-pack/Cargo.lock b/lib/rust/enso-pack/Cargo.lock deleted file mode 100644 index ae998406f7..0000000000 --- a/lib/rust/enso-pack/Cargo.lock +++ /dev/null @@ -1,7 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "enso-pack" -version = "0.1.0" diff --git a/lib/rust/enso-pack/Cargo.toml b/lib/rust/enso-pack/Cargo.toml deleted file mode 100644 index 05642bb59e..0000000000 --- a/lib/rust/enso-pack/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "enso-pack" -version = "0.1.0" -authors = ["Enso Team "] -edition = "2021" - -[lib] -crate-type = ["rlib"] - -[dependencies] -futures = { version = "0.3" } -ide-ci = { path = "../../../build/ci_utils" } -manifest-dir-macros = "0.1.16" -serde = { workspace = true } -serde_json = { workspace = true } -tokio = { workspace = true } -walkdir = "2" -enso-prelude = { path = "../prelude" } diff --git a/lib/rust/enso-pack/js/src/asset-extractor/args.ts b/lib/rust/enso-pack/js/src/asset-extractor/args.ts deleted file mode 100644 index 2056a297f0..0000000000 --- a/lib/rust/enso-pack/js/src/asset-extractor/args.ts +++ /dev/null @@ -1,117 +0,0 @@ -/** @file A simple argument parser. */ - -import * as util from 'node:util' - -// ========================== -// === Naming Conversions === -// ========================== - -/** Converts a camel case string to a kebab case string. */ -function camelToKebabCase(name: string) { - return name - .split('') - .map((letter, idx) => { - return letter.toUpperCase() === letter - ? `${idx !== 0 ? '-' : ''}${letter.toLowerCase()}` - : letter - }) - .join('') -} - -// ============== -// === Option === -// ============== - -class Option { - 'default': T | undefined - value: T | undefined - description: string - type: 'string' | 'boolean' - constructor(description: string, def?: T) { - this.default = def - this.description = description - if (def === true || def === false) { - this.type = 'boolean' - } else { - this.type = 'string' - } - } -} - -// ================= -// === ArgParser === -// ================= - -interface ParseArgsOptionConfig { - type: 'string' | 'boolean' - multiple?: boolean | undefined - short?: string | undefined - default?: string | boolean | string[] | boolean[] | undefined -} - -export class Args { - [key: string]: Option - help = new Option('Print help message.', false) - outDir = new Option('The directory the extracted asset sources will be written to.') -} - -export class ArgParser { - args = new Args() - - parse() { - const optionToFieldNameMap = new Map() - const options: Record = {} - for (const [fieldName, option] of Object.entries(this.args)) { - const optionName = camelToKebabCase(fieldName) - optionToFieldNameMap.set(optionName, fieldName) - options[optionName] = { type: option.type, default: option.default } - } - try { - const out = util.parseArgs({ options }) - for (const [optionName, optionValue] of Object.entries(out.values)) { - const fieldName = optionToFieldNameMap.get(optionName) - if (fieldName) { - // @ts-expect-error - this.args[fieldName].value = optionValue - } else { - console.error(`Unknown option: ${optionName}`) - process.exit(1) - } - } - } catch (error) { - const msg = error instanceof Error ? `${error.message}. ` : '' - console.error(`${msg}Use --help to learn about possible options.`) - process.exit(1) - } - if (this.args.help.value) { - this.printHelpAndExit(0) - } - } - - printHelp() { - console.log(`Options:`) - for (const [fieldName, option] of Object.entries(this.args)) { - const optionName = camelToKebabCase(fieldName) - let header = `--${optionName}` - if (option.type == 'string') { - const def = option.default != null ? `[${option.default}]` : '' - header += `=${def}` - } - console.log() - console.log(header) - console.log(option.description) - } - } - - printHelpAndExit(exitCode: number) { - this.printHelp() - process.exit(exitCode) - } -} - -/** Parse the command line arguments. */ -export function parse(): ArgParser { - const argParser = new ArgParser() - argParser.parse() - return argParser -} diff --git a/lib/rust/enso-pack/js/src/asset-extractor/asset-extractor.ts b/lib/rust/enso-pack/js/src/asset-extractor/asset-extractor.ts deleted file mode 100644 index 7ddff2b452..0000000000 --- a/lib/rust/enso-pack/js/src/asset-extractor/asset-extractor.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** @file Tool for extracting sources of dynamic assets from compiled WASM binaries. */ - -import path from 'path' -import * as args from './args' -import * as fs from './fs' -import * as log from '../runner/log' -import * as runner from '../runner/index' - -// =========== -// === App === -// =========== - -/** The main application. It loads the WASM file from disk, runs before main entry points, extract - * asset sources and saves them to files. */ -class App extends runner.App { - override async loadWasm() { - const mainJsUrl = path.join(__dirname, this.config.groups.loader.options.jsUrl.value) - const mainWasmUrl = path.join(__dirname, this.config.groups.loader.options.wasmUrl.value) - const mainJs = await fs.readFile(mainJsUrl, 'utf8') - const mainWasm = await fs.readFile(mainWasmUrl) - this.wasm = await this.compileAndRunWasm(mainJs, mainWasm) - } - - async extractAssets(outDir: string) { - await log.Task.asyncRun('Extracting dynamic assets source code.', async () => { - // Clear the extracted-sources directory before getting new sources. - // If getting sources fails we leave the directory empty, not outdated. - await fs.rm(outDir, { recursive: true, force: true }) - await fs.mkdir(outDir) - const assetsMap = this.getAssetSources() - if (assetsMap) { - await log.Task.asyncRun(`Writing assets to '${outDir}'.`, async () => { - for (const [builder, asset] of assetsMap) { - for (const [key, files] of asset) { - const dirPath = path.join(outDir, builder, key) - await fs.mkdir(dirPath, { recursive: true }) - for (const [name, data] of files) { - const filePath = path.join(dirPath, name) - await fs.writeFile(`${filePath}`, Buffer.from(data)) - } - } - } - }) - } - }) - } - - override async run(): Promise { - const parser = args.parse() - const outDir = parser.args.outDir.value - if (outDir) { - await log.Task.asyncRun('Running the program.', async () => { - await app.loadAndInitWasm() - const r = app.runBeforeMainEntryPoints().then(() => { - return app.extractAssets(outDir) - }) - await r - }) - } else { - parser.printHelpAndExit(1) - } - } -} - -// ============ -// === Main === -// ============ - -const app = new App() -void app.run() diff --git a/lib/rust/enso-pack/js/src/asset-extractor/fs.ts b/lib/rust/enso-pack/js/src/asset-extractor/fs.ts deleted file mode 100644 index fef5938445..0000000000 --- a/lib/rust/enso-pack/js/src/asset-extractor/fs.ts +++ /dev/null @@ -1,130 +0,0 @@ -/** @file This module redefines some `node:fs` functions with embedded logging, so it is easy to - * track what they do. */ - -import { - MakeDirectoryOptions, - Mode, - ObjectEncodingOptions, - OpenMode, - PathLike, - RmDirOptions, - RmOptions, -} from 'node:fs' -import { FileHandle } from 'fs/promises' -import { Abortable } from 'node:events' -import { promises as fs } from 'fs' -import * as log from '../runner/log' -import { Stream } from 'node:stream' - -// ================ -// === readFile === -// ================ - -export async function readFile( - path: PathLike | FileHandle, - options?: - | ({ - encoding?: null | undefined - flag?: OpenMode | undefined - } & Abortable) - | null -): Promise - -export async function readFile( - path: PathLike | FileHandle, - options: - | ({ - encoding: BufferEncoding - flag?: OpenMode | undefined - } & Abortable) - | BufferEncoding -): Promise - -/** Read a file and log the operation. */ -export async function readFile( - path: PathLike | FileHandle, - options?: - | (ObjectEncodingOptions & - Abortable & { - flag?: OpenMode | undefined - }) - | BufferEncoding - | null -): Promise { - return log.Task.asyncRun(`Reading file '${String(path)}'.`, async () => { - return await fs.readFile(path, options) - }) -} - -// ============== -// === unlink === -// ============== - -/** Unlink a file and log the operation. */ -export async function unlink(path: PathLike): Promise { - return log.Task.asyncRun(`Removing file '${String(path)}'.`, async () => { - return await fs.unlink(path) - }) -} - -// ============= -// === rmdir === -// ============= - -/** Remove a directory and log the operation. */ -export async function rmdir(path: PathLike, options?: RmDirOptions): Promise { - return log.Task.asyncRun(`Removing directory '${String(path)}'.`, async () => { - return await fs.rmdir(path, options) - }) -} - -// ============= -// === mkdir === -// ============= - -/** Make a directory and log the operation. */ -export async function mkdir( - path: PathLike, - options?: Mode | MakeDirectoryOptions | null -): Promise { - return log.Task.asyncRun(`Creating directory '${String(path)}'.`, async () => { - return await fs.mkdir(path, options) - }) -} - -// ========== -// === rm === -// ========== - -/** Remove a file or directory and log the operation. */ -export async function rm(path: PathLike, options?: RmOptions): Promise { - return log.Task.asyncRun(`Removing '${String(path)}'.`, async () => { - return await fs.rm(path, options) - }) -} - -// ================= -// === writeFile === -// ================= - -/** Write a file and log the operation. */ -export async function writeFile( - file: PathLike | FileHandle, - data: - | string - | NodeJS.ArrayBufferView - | Iterable - | AsyncIterable - | Stream, - options?: - | (ObjectEncodingOptions & { - mode?: Mode | undefined - flag?: OpenMode | undefined - } & Abortable) - | BufferEncoding - | null -): Promise { - return log.Task.asyncRun(`Writing file '${String(file)}'.`, async () => { - return await fs.writeFile(file, data, options) - }) -} diff --git a/lib/rust/enso-pack/js/src/runtime-libs/runtime-libs.ts b/lib/rust/enso-pack/js/src/runtime-libs/runtime-libs.ts deleted file mode 100644 index 66e5e1e5ca..0000000000 --- a/lib/rust/enso-pack/js/src/runtime-libs/runtime-libs.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** @file Contains {@link spector} function which is a wrapper for - * [spectorjs]{@link https://github.com/BabylonJS/Spector.js}. */ - -/* eslint @typescript-eslint/no-unsafe-return: "off" */ -/** Spectorjs function wrapper. */ -export function spector() { - return require('spectorjs') -} diff --git a/lib/rust/enso-pack/js/src/wasm-pack-bundle/index.ts b/lib/rust/enso-pack/js/src/wasm-pack-bundle/index.ts deleted file mode 100644 index 97de26106e..0000000000 --- a/lib/rust/enso-pack/js/src/wasm-pack-bundle/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** @file @{link pkg} init export. */ - -// Following imports are only valid in context of the built package. Here, those are guaranteed to -// not resolve correctly, so we need to disable the type checking. - -// @ts-expect-error -import init from './pkg.js' -// @ts-expect-error -export * from './runtime-libs' - -export { init } diff --git a/lib/rust/enso-pack/src/assets.rs b/lib/rust/enso-pack/src/assets.rs deleted file mode 100644 index 3d11c30e28..0000000000 --- a/lib/rust/enso-pack/src/assets.rs +++ /dev/null @@ -1,401 +0,0 @@ -//! Building dynamic assets (assets which require the application to be run to generate their -//! sources). -//! -//! The essential operation, producing a directory of outputs from a directory of inputs, is -//! implemented by each builder (e.g. [`Builder::Shader`], [`Builder::Font`]). -//! -//! As builders can take some time to run, a caching mechanism is used to avoid unnecessary -//! rebuilds. Caching is achieved by making populating-the-output-directory an idempotent process: -//! Paths within the output directory are dependent on the *content* of the corresponding input -//! files, so that if a calculated output path already exists, it is already up-to-date; otherwise, -//! it must be built. This design may be familiar to users of the Nix or Guix package managers. - -use ide_ci::prelude::*; - -use crate::Paths; - -use enso_prelude::anyhow; -use ide_ci::programs::shaderc::Glslc; -use ide_ci::programs::shaderc::SpirvOpt; -use ide_ci::programs::spirv_cross::SpirvCross; -use std::hash::Hasher; - - - -// ============= -// === Build === -// ============= - -/// Bring the dynamic assets up-to-date, for the current asset sources. This consists of: -/// - Scan the asset source directory tree, hashing the input files. -/// - Update the assets: -/// - For each asset-source directory, determine an output directory based on the inputs name and -/// the hashes of its files. -/// - If that output directory doesn't exist, run the builder (determined by the top-level -/// directory the in which the asset was found, e.g. `shader`) and populate the directory. -/// - Generate a manifest, identifying the current assets and paths to their sources. -pub async fn build(paths: &Paths) -> Result<()> { - info!("Building dynamic assets."); - let sources = survey_asset_sources(paths)?; - let assets = update_assets(paths, &sources).await?; - let manifest = serde_json::to_string(&assets)?; - ide_ci::fs::tokio::write(&paths.target.enso_pack.dist.dynamic_assets.manifest, manifest) - .await?; - gc_assets(paths, &assets)?; - Ok(()) -} - - -// =============== -// === Builder === -// =============== - -/// Identifies an asset type, which determines how it is built. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)] -#[serde(rename_all = "lowercase")] -enum Builder { - Font, - Shader, -} - -impl Builder { - fn dir_name<'a>(self) -> &'a str { - self.into() - } - - async fn build_asset( - self, - input_dir: &Path, - input_files: &[String], - output_dir: &Path, - tmp: &Path, - ) -> Result<()> { - match self { - Builder::Font => build_font(input_dir, input_files, output_dir).await, - Builder::Shader => build_shader(input_dir, input_files, output_dir, tmp).await, - } - } -} - -impl TryFrom<&str> for Builder { - type Error = anyhow::Error; - fn try_from(value: &str) -> std::result::Result { - match value { - "font" => Ok(Builder::Font), - "shader" => Ok(Builder::Shader), - other => Err(anyhow!("Unknown builder: {other:?}")), - } - } -} - -impl From for &'static str { - fn from(value: Builder) -> Self { - match value { - Builder::Font => "font", - Builder::Shader => "shader", - } - } -} - - -// ==================== -// === Build Inputs === -// ==================== - -/// The inputs to a builder. -struct AssetSources { - asset_key: String, - input_files: Vec, - inputs_hash: u64, -} - -impl AssetSources { - /// The output directory name for the asset. - fn dir_name(&self) -> String { - let key = &self.asset_key; - let hash = self.inputs_hash; - format!("{key}-{hash:x}") - } -} - - -// ===================== -// === Build Outputs === -// ===================== - -/// The outputs of a builder. -#[derive(Serialize)] -struct Asset { - dir: String, - files: Vec, -} - -/// The outputs of all builders. -type AssetManifest = BTreeMap>; - - -// ================ -// === Building === -// ================ - -/// Scan the sources found in the asset sources directory. -/// -/// Returns, for each [`Builder`] (e.g. shader or font), for each asset directory found, an -/// [`AssetSources`] object identifying the asset key (i.e. the name of its directory), its input -/// files, and a hash covering all its input files. -fn survey_asset_sources(paths: &Paths) -> Result>> { - let dir = ide_ci::fs::read_dir(&paths.target.enso_pack.dynamic_assets)?; - let mut asset_sources: HashMap<_, Vec<_>> = HashMap::new(); - let mut buf = Vec::new(); - for entry in dir { - let entry = entry?; - let builder = Builder::try_from(entry.file_name().to_string_lossy().as_ref())?; - let builder_dir = ide_ci::fs::read_dir(entry.path())?; - let builder_sources = asset_sources.entry(builder).or_default(); - for entry in builder_dir { - let entry = entry?; - let asset_key = entry.file_name().to_string_lossy().to_string(); - let dir = ide_ci::fs::read_dir(entry.path())?; - let mut file_hashes = BTreeMap::new(); - for entry in dir { - let entry = entry?; - let file_name = entry.file_name().to_string_lossy().to_string(); - let path = entry.path(); - buf.clear(); - ide_ci::fs::open(path)?.read_to_end(&mut buf)?; - let mut file_hasher = std::collections::hash_map::DefaultHasher::new(); - buf.hash(&mut file_hasher); - file_hashes.insert(file_name, file_hasher.finish()); - } - let mut asset_hasher = std::collections::hash_map::DefaultHasher::new(); - file_hashes.hash(&mut asset_hasher); - let inputs_hash = asset_hasher.finish(); - let input_files = file_hashes.into_keys().collect(); - builder_sources.push(AssetSources { asset_key, input_files, inputs_hash }); - } - } - Ok(asset_sources) -} - -/// Generate any assets not found up-to-date in the cache. -/// -/// If an output directory already exists, it can be assumed to be up-to-date (because output path -/// is dependent on the input data), and is used as-is. Otherwise, [`build_asset`] runs the -/// appropriate builder to generate the output directory. In either case, a summary of the files -/// present in the output directory is produced; these summaries are assembled into an -/// [`AssetManifest`]. -/// -/// When asset builders need to be invoked, they are all run in parallel. -async fn update_assets( - paths: &Paths, - sources: &HashMap>, -) -> Result { - let out = &paths.target.enso_pack.dist.dynamic_assets; - ide_ci::fs::create_dir_if_missing(out)?; - let mut assets: AssetManifest = BTreeMap::new(); - let mut deferred_assets: BTreeMap> = BTreeMap::new(); - for (&builder, builder_sources) in sources { - let out = out.join(builder.dir_name()); - ide_ci::fs::create_dir_if_missing(&out)?; - for source_specification in builder_sources { - let out = out.join(source_specification.dir_name()); - let key = source_specification.asset_key.clone(); - match std::fs::try_exists(&out)? { - false => { - info!("Rebuilding asset: `{}`.", out.display()); - let builder_assets = deferred_assets.entry(builder).or_default(); - let build = build_asset(paths, builder, source_specification); - builder_assets.push(async move { Ok((key, build.await?)) }); - } - true => { - debug!("Skipping clean asset: `{}`.", out.display()); - let builder_assets = assets.entry(builder).or_default(); - let asset = survey_asset(paths, builder, source_specification)?; - builder_assets.insert(key, asset); - } - }; - } - } - for (builder, deferred_assets) in deferred_assets.into_iter() { - let deferred_assets = - futures::stream::iter(deferred_assets).buffer_unordered(50).collect::>().await; - let deferred_assets: Result> = deferred_assets.into_iter().collect(); - assets.entry(builder).or_default().extend(deferred_assets?); - } - Ok(assets) -} - -/// Generate an asset from the given sources. -/// -/// Set up paths (as described in the [`crate`] docs): run the appropriate [`Builder`]; move its -/// output from a temporary path into its final location (note that outputs are not built directly -/// in their final location, because directories found in the output tree are assumed to -/// accurately represent the results of running the specified builder for the specified inputs; -/// creating the output directory in its complete state ensures that if a build process is -/// interrupted, incomplete artifacts are never used). -async fn build_asset( - paths: &Paths, - builder: Builder, - source_specification: &AssetSources, -) -> Result { - let input_dir = paths - .target - .enso_pack - .dynamic_assets - .join(builder.dir_name()) - .join(&source_specification.asset_key); - let tmp_output_dir = paths - .target - .enso_pack - .dist - .dynamic_assets - .join(builder.dir_name()) - .join(&source_specification.asset_key); - tokio::fs::create_dir(&tmp_output_dir).await?; - let work_path = paths - .target - .enso_pack - .dynamic_assets - .join(builder.dir_name()) - .join(format!("{}.work", source_specification.asset_key)); - builder - .build_asset(&input_dir, &source_specification.input_files, &tmp_output_dir, &work_path) - .await?; - let output_dir = paths - .target - .enso_pack - .dist - .dynamic_assets - .join(builder.dir_name()) - .join(source_specification.dir_name()); - tokio::fs::rename(tmp_output_dir, output_dir).await?; - survey_asset(paths, builder, source_specification) -} - -/// Identify the files present in an asset directory. -fn survey_asset( - paths: &Paths, - builder: Builder, - source_specification: &AssetSources, -) -> Result { - let dir = source_specification.dir_name(); - let path = paths.target.enso_pack.dist.dynamic_assets.join(builder.dir_name()).join(&dir); - let mut files = Vec::new(); - for entry in ide_ci::fs::read_dir(&path)? { - files.push(entry?.file_name().to_string_lossy().to_string()); - } - Ok(Asset { dir, files }) -} - -/// Remove any assets not present in the manifest. -fn gc_assets(paths: &Paths, assets: &AssetManifest) -> Result<()> { - let is_not_manifest = |entry: &std::io::Result| { - entry - .as_ref() - .map(|entry| entry.path() != paths.target.enso_pack.dist.dynamic_assets.manifest) - .unwrap_or(true) - }; - for entry in paths.target.enso_pack.dist.dynamic_assets.read_dir()?.filter(is_not_manifest) { - let entry = entry?; - let path = entry.path(); - let builder = Builder::try_from(entry.file_name().to_string_lossy().as_ref()).ok(); - let assets = builder.and_then(|builder| assets.get(&builder)); - match assets { - Some(assets) => { - let assets: HashSet<_> = assets.values().map(|asset| asset.dir.as_ref()).collect(); - for entry in path.read_dir()? { - let entry = entry?; - let path = entry.path(); - if !assets.contains(entry.file_name().to_string_lossy().as_ref()) { - info!("Cleaning unused asset at `{}`.", path.display()); - ide_ci::fs::remove_if_exists(path)?; - } - } - } - _ => { - info!("Cleaning unused builder at `{}`.", path.display()); - ide_ci::fs::remove_if_exists(path)?; - } - } - } - Ok(()) -} - - -// ============= -// === Fonts === -// ============= - -async fn build_font(input_dir: &Path, input_files: &[String], output_dir: &Path) -> Result<()> { - for file_name in input_files { - crate::copy(input_dir.join(file_name), output_dir.join(file_name))?; - } - Ok(()) -} - - -// =============== -// === Shaders === -// =============== - -/// Build optimized shaders by using `glslc`, `spirv-opt` and `spirv-cross`. -async fn build_shader( - input_dir: &Path, - input_files: &[String], - output_dir: &Path, - work_dir: &Path, -) -> Result<()> { - ide_ci::fs::tokio::create_dir_if_missing(work_dir).await?; - info!("Optimizing `{}`.", input_dir.file_name().unwrap_or_default().to_string_lossy()); - for glsl_file_name in input_files { - let glsl_path = input_dir.join(glsl_file_name); - let work_path = work_dir.join(glsl_file_name); - let stage_path = work_path.with_extension(""); - let stage = - stage_path.file_name().ok_or_else(|| anyhow!("Empty stage path."))?.to_string_lossy(); - let spv_path = stage_path.with_appended_extension("spv"); - let spv_opt_path = stage_path.with_appended_extension("opt.spv"); - let glsl_opt_path = stage_path.with_appended_extension("opt.glsl"); - let glsl_opt_dist_path = output_dir.join(glsl_file_name); - let spv_path = spv_path.as_str(); - let glsl_path = glsl_path.as_str(); - let shader_stage = &format!("-fshader-stage={stage}"); - let glslc_args = ["--target-env=opengl", shader_stage, "-o", spv_path, glsl_path]; - let spirv_opt_args = ["-O", "-o", spv_opt_path.as_str(), spv_path.as_str()]; - let spirv_cross_args = ["--output", glsl_opt_path.as_str(), spv_opt_path.as_str()]; - Glslc.cmd()?.args(glslc_args).run_ok().await?; - SpirvOpt.cmd()?.args(spirv_opt_args).run_ok().await?; - SpirvCross.cmd()?.args(spirv_cross_args).run_ok().await?; - - let content = - ide_ci::fs::tokio::read_to_string(&glsl_opt_path).await?.replace("\r\n", "\n"); - let extract_err = || format!("Failed to process shader '{}'.", glsl_opt_path.as_str()); - let code = extract_main_shader_code(&content).with_context(extract_err)?; - ide_ci::fs::tokio::write(&glsl_opt_dist_path, code).await?; - } - Ok(()) -} - -/// Read the optimized shader code, extract the main function body and preserve all top-level -/// variable declarations. -fn extract_main_shader_code(code: &str) -> Result { - let main_start_str = "void main()\n{"; - let main_end_str = "}"; - let main_fn_find_err = "Failed to find main function."; - let main_start = code.find(main_start_str).with_context(|| main_fn_find_err)?; - let main_end = code.rfind(main_end_str).with_context(|| main_fn_find_err)?; - let before_main = &code[..main_start]; - let declarations: Vec<&str> = before_main - .lines() - .filter_map(|line| { - let version_def = line.starts_with("#version "); - let precision_def = line.starts_with("precision "); - let layout_def = line.starts_with("layout("); - let def = version_def || precision_def || layout_def; - (!def).then_some(line) - }) - .collect(); - let declarations = declarations.join("\n"); - let main_content = &code[main_start + main_start_str.len()..main_end]; - Ok(format!("{declarations}\n{main_content}")) -} diff --git a/lib/rust/enso-pack/src/lib.rs b/lib/rust/enso-pack/src/lib.rs deleted file mode 100644 index 80ff8b8fdc..0000000000 --- a/lib/rust/enso-pack/src/lib.rs +++ /dev/null @@ -1,464 +0,0 @@ -//! EnsoGL Pack compiles Rust sources, builds the dynamic assets of the EnsoGL app (including -//! optimized shaders and pre-seeded caches), and outputs the JS WASM loader, additional JS runtime -//! utilities, and a set of optimized dynamic assets. It is a wrapper for `wasm-pack` tool. -//! -//! # Compilation process. -//! When run, the following file tree will be created/used. The files/directories marked with '*' -//! are required to be included with your final application code. The files marked with '**' are -//! recommended to be included. -//! -//! ```text -//! workspace | The main workspace directory (repo root). -//! ├─ ... / this_crate | This crate's directory. -//! │ ╰─ js | This crate's JS sources. -//! │ ├─ runner | Runner of WASM app. Used as a package by the app. -//! │ ├─ runtime-libs | Additional libs bundled with app. E.g. SpectorJS. -//! │ ├─ shader-extractor | App to extract shaders from WASM. -//! │ ╰─ wasm-pack-bundle | Glue for `wasm-pack` artifacts. -//! │ ╰─ index.ts | Copied to `target/ensogl-pack/wasm-pack/index.ts`. -//! ╰─ target | Directory where Rust and wasm-pack store build artifacts. -//! ╰─ ensogl-pack | Directory where ensogl-pack stores its build artifacts. -//! ├─ wasm-pack | Wasm-pack artifacts, re-created on every run. -//! │ ├─ pkg.js | Wasm-pack JS file to load WASM and glue it with snippets. -//! │ ├─ pkg_bg.wasm | Wasm-pack WASM bundle. -//! │ ├─ index.ts | Main file, copied from `this_crate/js/wasm-pack-bundle`. -//! │ ├─ runtime-libs.js | Bundled `this_crate/js/runtime-libs`. -//! │ ╰─ snippets | Rust-extracted JS snippets. -//! │ ╰─ .js | A single Rust-extracted JS snippet. -//! ├─ dynamic-assets | Dynamic asset sources extracted from WASM bundle. -//! │ ├─ shader | Pre-compiled shaders. -//! │ │ ├─ | Asset sources (the GLSL file). -//! │ │ ├─ .work | Intermediate files produced by the shader compiler. -//! │ │ ╰─ ... -//! │ ├─ font | Pre-generated MSDF data. -//! │ │ ├─ | Asset sources (the glyph atlas image, and metadata). -//! │ │ ╰─ ... -//! │ ╰─ ... -//! ├─ runtime-libs -//! │ ╰─ runtime-libs.js -//! ╰─ dist | Final build artifacts of ensogl-pack. -//! * ├─ index.js | The main JS bundle to load WASM and JS wasm-pack bundles. -//! ├─ index.js.map | The sourcemap mapping to sources in TypeScript. -//! ** ├─ index.d.ts | TypeScript types interface file. -//! ├─ asset-extractor.cjs | Node program to extract asset sources from WASM. -//! ├─ asset-extractor.cjs.map | The sourcemap mapping to sources in TypeScript. -//! * ├─ pkg.js | The `pks.js` artifact of wasm-pack WITH bundled snippets. -//! ├─ pkg.js.map | The sourcemap mapping to `pkg.js` generated by wasm-pack. -//! * ├─ pkg.wasm | The `pks_bg.wasm` artifact of wasm-pack. -//! * ╰─ dynamic-assets | Built dynamic assets. -//! ├─ manifest.json | An index of all the assets and their files. -//! ├─ shader | Pre-compiled shaders. -//! │ ├─ | A subdirectory for each asset. -//! │ ╰─ ... -//! ├─ font | Pre-generated MSDF data. -//! │ ├─ | A subdirectory for each asset. -//! │ ╰─ ... -//! ╰─ ... -//! ``` -//! -//! The high-level app compilation process is summarized below: -//! -//! 1. If the `dist/index.js` file does not exist, or its modification date is older than -//! `this_crate/js` sources: -//! -//! 1. `npm install` is assumed to have been already run in the `this_crate/js` directory. -//! -//! 2. The `this_crate/js/runner` is compiled to `target/ensogl-pack/dist/index.cjs`. This is the -//! main file which is capable of loading WASM file, displaying a loading screen, running -//! before-main entry points, and running the main entry point of the application. -//! -//! 3. The `this_crate/js/shader-extractor` is compiled to -//! `target/ensogl-pack/dist/shader-extractor.js`. This is a node program that extracts -//! non-optimized shaders from the WASM file. -//! -//! 4. The `this_crate/js/runtime-libs` is compiled to -//! `target/ensogl-pack/runtime-libs/runtime-libs.js`. This is a bundle containing additional JS -//! libs, such as SpectorJS. -//! -//! 2. The rust sources are build with `wasm-pack`, which produces the following artifacts: -//! `target/ensogl-pack/wasm-pack/{pkg.js, pkg_bg.wasm, snippets}`. The file `pkg_bg.wasm` is copied -//! to `target/ensogl-pack/dist/pkg.wasm`. -//! -//! 3. The file `this_crate/js/wasm-pack-bundle/index.ts` is copied to -//! `target/ensogl-pack/wasm-pack/index.ts`. This is the main file which when compiled glues -//! `pkg.js`, `snippets`, and `runtime-libs.js` into a single bundle. -//! -//! 4. The program `target/ensogl-pack/dist/asset-extractor.cjs` is run. It loads -//! `target/dist/pkg.wasm` and writes asset sources to `target/ensogl-pack/dynamic-assets`. -//! -//! 5. For each asset, its inputs are hashed and an output directory is determined based on its -//! name and input hash. If the output directory doesn't already exist, the asset is built, and the -//! result is written to `dist/dynamic-assets`. The manifest is rebuilt to reflect the current set -//! of asset outputs, and any outdated output directories are removed. -//! -//! 6. The `target/ensogl-pack/wasm-pack/index.ts` is compiled to -//! `target/ensogl-pack/dist/index.js`. -//! -//! -//! -//! # Runtime process. -//! When `target/dist/index.js` is run: -//! -//! 1. The following files are downloaded from a server: -//! `target/dist/{pkg.js, pkg.wasm, dynamic-assets}`. -//! 2. The code from `pkg.js` is run to compile the WASM file. -//! 3. All before-main entry points are run. -//! 4. Optimized shaders are uploaded to the EnsoGL application. -//! 5. The main entry point is run. - -// === Features === -#![feature(async_closure)] -#![feature(fs_try_exists)] -// === Standard Linter Configuration === -#![deny(non_ascii_idents)] -#![warn(unsafe_code)] -#![allow(clippy::bool_to_int_with_if)] -#![allow(clippy::let_and_return)] -// === Non-Standard Linter Configuration === -#![warn(missing_docs)] - -use ide_ci::prelude::*; - -use ide_ci::program::EMPTY_ARGS; -use ide_ci::programs::wasm_pack::WasmPackCommand; -use manifest_dir_macros::path; -use std::env; -use std::path::Path; -use std::path::PathBuf; -use walkdir::WalkDir; - - -// ============== -// === Export === -// ============== - -pub mod assets; - -pub use ide_ci::prelude; - - - -// ================= -// === Hot Fixes === -// ================= - -/// A hot-fix for a bug on macOS, where `std::fs::copy` causes cargo-watch to loop infinitely. -/// See: https://github.com/watchexec/cargo-watch/issues/242 -pub fn copy(source_file: impl AsRef, destination_file: impl AsRef) -> Result { - if env::consts::OS == "macos" { - Command::new("cp").arg(source_file.as_ref()).arg(destination_file.as_ref()).spawn()?; - Ok(()) - } else { - ide_ci::fs::copy(source_file, destination_file) - } -} - - -// ============= -// === Paths === -// ============= - -/// Paths of the directories and files used by `ensogl-pack`. This struct maps to variables the -/// directory layout described in the docs of this module. -#[derive(Debug, Default)] -#[allow(missing_docs)] -pub struct Paths { - pub workspace: PathBuf, - pub this_crate: paths::ThisCrate, - pub target: paths::Target, -} - -macro_rules! define_paths { - ($( - $name:ident { - $($field:ident : $field_ty:ty),* $(,)? - } - )*) => {$( - #[derive(Debug, Default)] - #[allow(missing_docs)] - pub struct $name { - pub root: PathBuf, - $( pub $field: $field_ty ),* - } - - impl Deref for $name { - type Target = PathBuf; - fn deref(&self) -> &Self::Target { - &self.root - } - } - - impl AsRef for $name { - fn as_ref(&self) -> &std::path::Path { - &self.root - } - } - - impl AsRef for $name { - fn as_ref(&self) -> &OsStr { - self.root.as_ref() - } - } - )*}; -} - -/// Paths used during build. -pub mod paths { - use super::*; - define_paths! { - ThisCrate { - js: ThisCrateJs, - } - - ThisCrateJs { - wasm_pack_bundle: ThisCrateJsWasmPackBundle, - } - - ThisCrateJsWasmPackBundle { - index: PathBuf - } - - Target { - enso_pack: TargetEnsoglPack, - } - - TargetEnsoglPack { - wasm_pack: TargetEnsoglPackWasmPack, - dynamic_assets: PathBuf, - runtime_libs: TargetEnsoglPackRuntimeLibs, - dist: TargetEnsoglPackDist, - } - - TargetEnsoglPackRuntimeLibs { - runtime_libs: PathBuf, - } - - TargetEnsoglPackWasmPack { - index: PathBuf, - pkg_bg: PathBuf, - pkg_js: PathBuf, - runtime_libs: PathBuf, - } - - TargetEnsoglPackDist { - asset_extractor: PathBuf, - pkg_js: PathBuf, - main_wasm: PathBuf, - dynamic_assets: TargetEnsoglPackDistDynamicAssets, - } - - TargetEnsoglPackDistDynamicAssets { - manifest: PathBuf, - } - } -} - -const WASM_PACK_OUT_NAME: &str = "pkg"; - -impl Paths { - /// Create a set of paths values. - pub async fn new() -> Result { - let mut p = Paths::default(); - let current_cargo_path = Path::new(path!("Cargo.toml")); - p.this_crate.root = current_cargo_path.try_parent()?.into(); - p.this_crate.js.root = p.this_crate.join("js"); - p.this_crate.js.wasm_pack_bundle.root = - p.this_crate.js.root.join("src").join("wasm-pack-bundle"); - p.this_crate.js.wasm_pack_bundle.index = p.this_crate.js.wasm_pack_bundle.join("index.ts"); - p.workspace = workspace_dir().await?; - p.target.root = p.workspace.join("target"); - p.target.enso_pack.root = p.target.join("ensogl-pack"); - p.target.enso_pack.wasm_pack.root = p.target.enso_pack.join("wasm-pack"); - let pkg_wasm = format!("{WASM_PACK_OUT_NAME}_bg.wasm"); - let pkg_js = format!("{WASM_PACK_OUT_NAME}.js"); - p.target.enso_pack.wasm_pack.index = p.target.enso_pack.wasm_pack.join("index.ts"); - p.target.enso_pack.wasm_pack.pkg_bg = p.target.enso_pack.wasm_pack.join(pkg_wasm); - p.target.enso_pack.wasm_pack.pkg_js = p.target.enso_pack.wasm_pack.join(pkg_js); - p.target.enso_pack.wasm_pack.runtime_libs = - p.target.enso_pack.wasm_pack.join("runtime-libs.js"); - p.target.enso_pack.dynamic_assets = p.target.enso_pack.join("dynamic-assets"); - p.target.enso_pack.runtime_libs.root = p.target.enso_pack.join("runtime-libs"); - p.target.enso_pack.runtime_libs.runtime_libs = - p.target.enso_pack.runtime_libs.join("runtime-libs.js"); - p.target.enso_pack.dist.root = p.target.enso_pack.join("dist"); - p.target.enso_pack.dist.asset_extractor = - p.target.enso_pack.dist.join("asset-extractor.cjs"); - p.target.enso_pack.dist.pkg_js = p.target.enso_pack.dist.join("pkg.js"); - p.target.enso_pack.dist.main_wasm = p.target.enso_pack.dist.join("pkg.wasm"); - p.target.enso_pack.dist.dynamic_assets.root = - p.target.enso_pack.dist.join("dynamic-assets"); - p.target.enso_pack.dist.dynamic_assets.manifest = - p.target.enso_pack.dist.dynamic_assets.join("manifest.json"); - Ok(p) - } -} - -/// Returns the workspace directory (repo root). -pub async fn workspace_dir() -> Result { - use ide_ci::programs::cargo; - use ide_ci::programs::Cargo; - let output = Cargo - .cmd()? - .apply(&cargo::Command::LocateProject) - .apply(&cargo::LocateProjectOption::Workspace) - .apply(&cargo::LocateProjectOption::MessageFormat(cargo::MessageFormat::Plain)) - .output_ok() - .await? - .into_stdout_string()?; - let cargo_path = Path::new(output.trim()); - Ok(cargo_path.try_parent()?.to_owned()) -} - - -// ============= -// === Build === -// ============= - -/// The arguments to `wasm-pack build` that `ensogl-pack` wants to customize. -pub struct WasmPackOutputs { - /// Value to passed as `--out-dir` to `wasm-pack`. - pub out_dir: PathBuf, - /// Value to passed as `--out-name` to `wasm-pack`. - pub out_name: String, -} - -/// Check the modification time of all files in this crate's `js` directory and compare them with -/// the modification time of dist artifacts, if any. Do not traverse `node_modules` directory. -fn check_if_ts_needs_rebuild(paths: &Paths) -> Result { - let walk = WalkDir::new(&paths.this_crate.js).into_iter(); - let walk_no_node_modules = walk.filter_entry(|e| e.file_name() != "node_modules"); - let mut newest_mod_time: Option = None; - for opt_entry in walk_no_node_modules { - let entry = opt_entry?; - if entry.file_type().is_file() { - let metadata = entry.metadata()?; - let mod_time = metadata.modified()?; - newest_mod_time = Some(newest_mod_time.map_or(mod_time, |t| t.max(mod_time))); - } - } - if let Ok(app_js_metadata) = std::fs::metadata(&paths.target.enso_pack.dist.asset_extractor) { - let app_js_mod_time = app_js_metadata.modified()?; - Ok(newest_mod_time.map_or(true, |t| t > app_js_mod_time)) - } else { - Ok(true) - } -} - -/// Compile TypeScript sources of this crate in case they were not compiled yet. -pub async fn compile_this_crate_ts_sources(paths: &Paths) -> Result<()> { - println!("compile_this_crate_ts_sources"); - if check_if_ts_needs_rebuild(paths)? { - info!("EnsoGL Pack TypeScript sources changed, recompiling."); - let run_script = async move |script_name, script_args: &[&str]| { - ide_ci::programs::Npm - .cmd()? - .run(script_name) - .args(script_args) - .current_dir(&paths.this_crate.js) - .run_ok() - .await - }; - - info!("Linting TypeScript sources."); - run_script("lint", &EMPTY_ARGS).await?; - - info!("Building TypeScript sources."); - let args = ["--", &format!("--out-dir={}", paths.target.enso_pack.dist.display())]; - run_script("build-asset-extractor", &args).await?; - println!("BUILD build-runtime-libs"); - let args = ["--", &format!("--outdir={}", paths.target.enso_pack.runtime_libs.display())]; - run_script("build-runtime-libs", &args).await?; - } else { - println!("NO BUILD"); - } - Ok(()) -} - -/// Run wasm-pack to build the wasm artifact. -#[context("Failed to run wasm-pack.")] -pub async fn run_wasm_pack( - paths: &Paths, - provider: impl FnOnce(WasmPackOutputs) -> Result, -) -> Result<()> { - info!("Obtaining and running the wasm-pack command."); - let replaced_args = WasmPackOutputs { - out_dir: paths.target.enso_pack.wasm_pack.root.clone(), - out_name: WASM_PACK_OUT_NAME.to_string(), - }; - let mut command = provider(replaced_args).context("Failed to obtain wasm-pack command.")?; - command.run_ok().await?; - - copy(&paths.this_crate.js.wasm_pack_bundle.index, &paths.target.enso_pack.wasm_pack.index)?; - copy( - &paths.target.enso_pack.runtime_libs.runtime_libs, - &paths.target.enso_pack.wasm_pack.runtime_libs, - )?; - - compile_wasm_pack_artifacts( - &paths.target.enso_pack.wasm_pack, - &paths.target.enso_pack.wasm_pack.index, - &paths.target.enso_pack.dist.pkg_js, - ) - .await?; - ide_ci::fs::copy( - &paths.target.enso_pack.wasm_pack.pkg_bg, - &paths.target.enso_pack.dist.main_wasm, - ) -} - -/// Compile wasm-pack artifacts (JS sources and snippets) to a single bundle. -async fn compile_wasm_pack_artifacts(pwd: &Path, pkg_js: &Path, out: &Path) -> Result { - info!("Compiling {}.", pkg_js.display()); - ide_ci::programs::Npx - .cmd()? - .args([ - "--yes", - "esbuild", - pkg_js.display().to_string().as_str(), - "--format=cjs", - "--bundle", - "--sourcemap", - "--platform=node", - &format!("--outfile={}", out.display()), - ]) - .current_dir(pwd) - .run_ok() - .await -} - -/// Extract asset sources from the WASM artifact. -async fn extract_assets(paths: &Paths) -> Result<()> { - info!("Extracting asset sources from generated WASM file."); - ide_ci::programs::Node - .cmd()? - .arg(&paths.target.enso_pack.dist.asset_extractor) - .arg("--out-dir") - .arg(&paths.target.enso_pack.dynamic_assets) - .run_ok() - .await -} - -/// Just builds the TypeScript sources. -pub async fn build_ts_sources_only() -> Result { - let paths = Paths::new().await?; - compile_this_crate_ts_sources(&paths).await -} - -/// Wrapper over `wasm-pack build` command. -/// -/// # Arguments -/// * `outputs` - The outputs that'd be usually given to `wasm-pack build` command. -/// * `provider` - Function that generates an invocation of the `wasm-pack build` command that has -/// applied given (customized) output-related arguments. -pub async fn build( - outputs: WasmPackOutputs, - provider: impl FnOnce(WasmPackOutputs) -> Result, -) -> Result { - let paths = Paths::new().await?; - compile_this_crate_ts_sources(&paths).await?; - run_wasm_pack(&paths, provider).await?; - extract_assets(&paths).await?; - assets::build(&paths).await?; - let out_dir = Path::new(&outputs.out_dir); - ide_ci::fs::copy(&paths.target.enso_pack.dist, out_dir) -} diff --git a/package-lock.json b/package-lock.json index e29e6ade69..531d0d658f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,8 @@ "workspaces": [ "app/ide-desktop", "app/ide-desktop/lib/*", - "app/gui2", - "lib/rust/enso-pack/js" + "lib/js/runner", + "app/gui2" ], "dependencies": { "chromedriver": "^106.0.1", @@ -190,6 +190,8 @@ "esbuild": "^0.19.3", "fast-glob": "^3.2.12", "portfinder": "^1.0.32", + "sharp": "^0.31.2", + "to-ico": "^1.1.5", "tsx": "^4.7.1" }, "optionalDependencies": { @@ -206,6 +208,7 @@ "app/ide-desktop/lib/content": { "name": "enso-content", "version": "1.0.0", + "extraneous": true, "dependencies": { "@types/semver": "^7.3.9", "enso-content-config": "^1.0.0", @@ -286,6 +289,7 @@ "esbuild": "^0.19.3", "esbuild-plugin-inline-image": "^0.0.9", "esbuild-plugin-time": "^1.0.0", + "esbuild-plugin-yaml": "^0.0.1", "eslint": "^8.49.0", "eslint-plugin-jsdoc": "^46.8.1", "eslint-plugin-react": "^7.32.1", @@ -316,10 +320,56 @@ "name": "enso-icons", "version": "1.0.0", "devDependencies": { + "@types/sharp": "^0.31.1", + "@types/to-ico": "^1.1.1", "sharp": "^0.31.2", "to-ico": "^1.1.5" } }, + "app/ide-desktop/lib/js": { + "name": "enso-runner", + "version": "1.0.0", + "extraneous": true, + "dependencies": { + "spectorjs": "^0.9.27" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^6.7.2", + "@typescript-eslint/parser": "^6.7.2", + "esbuild": "^0.19.3", + "eslint": "^8.49.0", + "eslint-plugin-jsdoc": "^46.8.1", + "tsup": "^7.2.0", + "typescript": "~5.2.2" + }, + "optionalDependencies": { + "esbuild-darwin-64": "^0.15.18", + "esbuild-linux-64": "^0.15.18", + "esbuild-windows-64": "^0.15.18" + } + }, + "app/ide-desktop/lib/runner": { + "name": "enso-runner", + "version": "1.0.0", + "extraneous": true, + "dependencies": { + "spectorjs": "^0.9.27" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^6.7.2", + "@typescript-eslint/parser": "^6.7.2", + "esbuild": "^0.19.3", + "eslint": "^8.49.0", + "eslint-plugin-jsdoc": "^46.8.1", + "tsup": "^7.2.0", + "typescript": "~5.2.2" + }, + "optionalDependencies": { + "esbuild-darwin-64": "^0.15.18", + "esbuild-linux-64": "^0.15.18", + "esbuild-windows-64": "^0.15.18" + } + }, "app/ide-desktop/lib/ts-plugin-namespace-auto-import": { "version": "1.0.0", "devDependencies": { @@ -368,6 +418,69 @@ "zen-observable-ts": "0.8.19" } }, + "app/ide-desktop/node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "app/ide-desktop/node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "app/ide-desktop/node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "app/ide-desktop/node_modules/@eslint/js": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", + "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "app/ide-desktop/node_modules/@types/sharp": { + "version": "0.31.1", + "resolved": "https://registry.npmjs.org/@types/sharp/-/sharp-0.31.1.tgz", + "integrity": "sha512-5nWwamN9ZFHXaYEincMSuza8nNfOof8nmO+mcI+Agx1uMUk4/pQnNIcix+9rLPXzKrm1pS34+6WRDbDV0Jn7ag==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "app/ide-desktop/node_modules/ajv": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", @@ -395,6 +508,143 @@ "js-cookie": "^2.2.1" } }, + "app/ide-desktop/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "app/ide-desktop/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "app/ide-desktop/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "app/ide-desktop/node_modules/eslint": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", + "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.49.0", + "@humanwhocodes/config-array": "^0.11.11", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "app/ide-desktop/node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "app/ide-desktop/node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "app/ide-desktop/node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "app/ide-desktop/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "app/ide-desktop/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -405,9 +655,31 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "lib/js/runner": { + "name": "enso-runner", + "version": "1.0.0", + "dependencies": { + "spectorjs": "^0.9.27" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^6.7.2", + "@typescript-eslint/parser": "^6.7.2", + "esbuild": "^0.19.3", + "eslint": "^8.49.0", + "eslint-plugin-jsdoc": "^46.8.1", + "tsup": "^7.2.0", + "typescript": "~5.2.2" + }, + "optionalDependencies": { + "esbuild-darwin-64": "^0.15.18", + "esbuild-linux-64": "^0.15.18", + "esbuild-windows-64": "^0.15.18" + } + }, "lib/rust/enso-pack/js": { "name": "enso-runner", "version": "1.0.0", + "extraneous": true, "dependencies": { "spectorjs": "^0.9.27" }, @@ -1966,15 +2238,6 @@ "node": ">=16" } }, - "node_modules/@esbuild-plugins/node-globals-polyfill": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.2.3.tgz", - "integrity": "sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==", - "dev": true, - "peerDependencies": { - "esbuild": "*" - } - }, "node_modules/@esbuild-plugins/node-modules-polyfill": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.2.2.tgz", @@ -3592,15 +3855,6 @@ "@types/responselike": "^1.0.0" } }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/cookie": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz", @@ -3936,12 +4190,6 @@ "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true - }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -4005,12 +4253,6 @@ "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", "dev": true }, - "node_modules/@types/mime": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.4.tgz", - "integrity": "sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==", - "dev": true - }, "node_modules/@types/mime-types": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", @@ -4023,15 +4265,6 @@ "dev": true, "optional": true }, - "node_modules/@types/morgan": { - "version": "1.9.9", - "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.9.tgz", - "integrity": "sha512-iRYSDKVaC6FkGSpEVVIvrRGw0DfJMiQzIn3qr2G5B3C//AWkulhXgaBd7tS9/J79GWSYMTHGs7PfI5b3Y8m+RQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/ms": { "version": "0.7.34", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", @@ -4130,27 +4363,8 @@ "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==" - }, - "node_modules/@types/serve-static": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", - "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", - "dev": true, - "dependencies": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/@types/sharp": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@types/sharp/-/sharp-0.31.1.tgz", - "integrity": "sha512-5nWwamN9ZFHXaYEincMSuza8nNfOof8nmO+mcI+Agx1uMUk4/pQnNIcix+9rLPXzKrm1pS34+6WRDbDV0Jn7ag==", - "dev": true, - "dependencies": { - "@types/node": "*" - } + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true }, "node_modules/@types/shuffle-seed": { "version": "1.1.3", @@ -6556,6 +6770,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "dev": true, "engines": { "node": ">=6" } @@ -8427,10 +8642,6 @@ "resolved": "app/ide-desktop/lib/common", "link": true }, - "node_modules/enso-content": { - "resolved": "app/ide-desktop/lib/content", - "link": true - }, "node_modules/enso-content-config": { "resolved": "app/ide-desktop/lib/content-config", "link": true @@ -8452,7 +8663,7 @@ "link": true }, "node_modules/enso-runner": { - "resolved": "lib/rust/enso-pack/js", + "resolved": "lib/js/runner", "link": true }, "node_modules/entities": { @@ -14903,6 +15114,7 @@ "version": "9.1.3", "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz", "integrity": "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==", + "dev": true, "dependencies": { "clsx": "^1.1.1" }, diff --git a/package.json b/package.json index ca92c767ce..ee6fb14719 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,8 @@ "workspaces": [ "app/ide-desktop", "app/ide-desktop/lib/*", - "app/gui2", - "lib/rust/enso-pack/js" + "lib/js/runner", + "app/gui2" ], "overrides": { "tslib": "$tslib"