From ffa81dc3152e4d39efa4a8cbc9ba42f510c0ebf9 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Tue, 28 Jul 2026 23:23:02 +0800 Subject: [PATCH 01/29] Add repository field to package.jsons --- .vscode/settings.json | 3 +-- package.json | 1 + src/common/cse-machine/package.json | 5 +++++ src/common/data-visualizer/package.json | 5 +++++ src/common/module-loader/package.json | 5 +++++ src/common/stepper/package.json | 5 +++++ src/common/tabs/package.json | 5 +++++ src/common/test/package.json | 5 +++++ src/runner/cse-machine/package.json | 5 +++++ src/runner/data-visualizer/package.json | 5 +++++ src/runner/module-loader/package.json | 5 +++++ src/runner/remoteExecution/package.json | 5 +++++ src/runner/stepper/package.json | 5 +++++ src/runner/test/package.json | 5 +++++ src/web/cse-machine/package.json | 5 +++++ src/web/data-visualizer/package.json | 5 +++++ src/web/module-loader/package.json | 5 +++++ src/web/stepper/package.json | 5 +++++ src/web/test/package.json | 5 +++++ yarn.config.cjs | 22 ++++++++++++++++++++++ yarn.lock | 10 ++++++++++ 21 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 yarn.config.cjs diff --git a/.vscode/settings.json b/.vscode/settings.json index d8d3bef..a357ac5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,7 +3,6 @@ "**/.yarn": true, "**/.pnp.*": true }, - "typescript.tsdk": ".yarn/sdks/typescript/lib", - "typescript.enablePromptUseWorkspaceTsdk": true, + "js/ts.enablePromptUseWorkspaceTsdk": true, "js/ts.tsdk.path": ".yarn/sdks/typescript/lib" } diff --git a/package.json b/package.json index f2a8d6b..cef7f3c 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@sourceacademy/plugin-directory": "source-academy/plugin-directory#main", "@types/node": "^25.9.1", "@vitest/coverage-istanbul": "^4.1.9", + "@yarnpkg/types": "^4.0.1", "commander": "^15.0.0", "eslint": "^10.0.3", "eslint-config-prettier": "^10.1.8", diff --git a/src/common/cse-machine/package.json b/src/common/cse-machine/package.json index a2fdb56..d762842 100644 --- a/src/common/cse-machine/package.json +++ b/src/common/cse-machine/package.json @@ -2,6 +2,11 @@ "name": "@sourceacademy/common-cse-machine", "version": "0.3.0", "packageManager": "yarn@4.12.0", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/common/cse-machine" + }, "description": "Shared protocol/types for the Source Academy CSE machine plugin pair", "scripts": { "build": "rollup -c", diff --git a/src/common/data-visualizer/package.json b/src/common/data-visualizer/package.json index 53e1c25..6da04b4 100644 --- a/src/common/data-visualizer/package.json +++ b/src/common/data-visualizer/package.json @@ -2,6 +2,11 @@ "name": "@sourceacademy/common-data-visualizer", "version": "0.0.1", "packageManager": "yarn@4.12.0", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/common/data-visualizer" + }, "description": "Shared, language-agnostic protocol for the Source Academy data visualizer plugin pair", "scripts": { "build": "rollup -c", diff --git a/src/common/module-loader/package.json b/src/common/module-loader/package.json index ec30676..5c479b4 100644 --- a/src/common/module-loader/package.json +++ b/src/common/module-loader/package.json @@ -3,6 +3,11 @@ "version": "0.0.1", "packageManager": "yarn@4.12.0", "description": "The common types for loading modules in Source Academy", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/common/module-loader" + }, "scripts": { "build": "rollup -c", "prepack": "yarn build" diff --git a/src/common/stepper/package.json b/src/common/stepper/package.json index c0e7616..450c610 100644 --- a/src/common/stepper/package.json +++ b/src/common/stepper/package.json @@ -3,6 +3,11 @@ "version": "0.0.2", "packageManager": "yarn@4.12.0", "description": "Shared, language-agnostic protocol for the Source Academy stepper plugin pair", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/common/stepper" + }, "scripts": { "build": "rollup -c", "prepack": "yarn build", diff --git a/src/common/tabs/package.json b/src/common/tabs/package.json index 332373e..1222f77 100644 --- a/src/common/tabs/package.json +++ b/src/common/tabs/package.json @@ -3,6 +3,11 @@ "version": "0.0.1", "packageManager": "yarn@4.12.0", "description": "Common utilities for tab loading in Source Academy", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/common/tabs" + }, "scripts": { "build": "rollup -c", "prepack": "yarn build" diff --git a/src/common/test/package.json b/src/common/test/package.json index 0c59298..fd62005 100644 --- a/src/common/test/package.json +++ b/src/common/test/package.json @@ -3,6 +3,11 @@ "version": "0.0.1", "packageManager": "yarn@4.12.0", "description": "The test common plugin for the plugins repository", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/common/test" + }, "scripts": { "build": "rollup -c", "prepack": "yarn build" diff --git a/src/runner/cse-machine/package.json b/src/runner/cse-machine/package.json index e173337..9cb66cf 100644 --- a/src/runner/cse-machine/package.json +++ b/src/runner/cse-machine/package.json @@ -3,6 +3,11 @@ "version": "3.0.0", "packageManager": "yarn@4.12.0", "description": "The runner-side CSE machine plugin: sends evaluation snapshots to the host", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/runner/cse-machine" + }, "scripts": { "build": "rollup -c", "prepack": "yarn build" diff --git a/src/runner/data-visualizer/package.json b/src/runner/data-visualizer/package.json index 9b3ba8e..1085829 100644 --- a/src/runner/data-visualizer/package.json +++ b/src/runner/data-visualizer/package.json @@ -3,6 +3,11 @@ "version": "0.0.1", "packageManager": "yarn@4.12.0", "description": "Language-agnostic runner plugin for the Source Academy data visualizer", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/runner/data-visualizer" + }, "scripts": { "build": "rollup -c", "prepack": "yarn build", diff --git a/src/runner/module-loader/package.json b/src/runner/module-loader/package.json index afcfa2e..5c68103 100644 --- a/src/runner/module-loader/package.json +++ b/src/runner/module-loader/package.json @@ -3,6 +3,11 @@ "version": "0.1.0", "packageManager": "yarn@4.12.0", "description": "The runner plugin for the module loader in Source Academy", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/runner/module-loader" + }, "scripts": { "build": "rollup -c", "prepack": "yarn build" diff --git a/src/runner/remoteExecution/package.json b/src/runner/remoteExecution/package.json index 4cabf6a..dbbe455 100644 --- a/src/runner/remoteExecution/package.json +++ b/src/runner/remoteExecution/package.json @@ -2,6 +2,11 @@ "name": "@sourceacademy/runner-remote-execution", "version": "0.0.1", "license": "ISC", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/runner/remoteExecution" + }, "publishConfig": { "access": "public" }, diff --git a/src/runner/stepper/package.json b/src/runner/stepper/package.json index 016eadc..70273dc 100644 --- a/src/runner/stepper/package.json +++ b/src/runner/stepper/package.json @@ -3,6 +3,11 @@ "version": "0.0.1", "packageManager": "yarn@4.12.0", "description": "Language-agnostic runner plugin for the Source Academy stepper", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/runner/stepper" + }, "scripts": { "build": "rollup -c", "prepack": "yarn build", diff --git a/src/runner/test/package.json b/src/runner/test/package.json index 6155072..1add4e4 100644 --- a/src/runner/test/package.json +++ b/src/runner/test/package.json @@ -3,6 +3,11 @@ "version": "0.0.1", "packageManager": "yarn@4.12.0", "description": "The test runner plugin for the plugins repository", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/runner/test" + }, "scripts": { "build": "rollup -c", "prepack": "yarn build" diff --git a/src/web/cse-machine/package.json b/src/web/cse-machine/package.json index 35b0f3a..2ffd854 100644 --- a/src/web/cse-machine/package.json +++ b/src/web/cse-machine/package.json @@ -3,6 +3,11 @@ "version": "3.0.0", "packageManager": "yarn@4.12.0", "description": "The web/host-side CSE machine plugin: receives evaluation snapshots for rendering", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/web/cse-machine" + }, "scripts": { "build": "rollup -c", "prepack": "yarn build" diff --git a/src/web/data-visualizer/package.json b/src/web/data-visualizer/package.json index 0d88e4f..b3c2d2b 100644 --- a/src/web/data-visualizer/package.json +++ b/src/web/data-visualizer/package.json @@ -3,6 +3,11 @@ "version": "0.0.1", "packageManager": "yarn@4.12.0", "description": "Host (web) plugin for the Source Academy data visualizer: classifies and renders drawn structures", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/web/data-visualizer" + }, "scripts": { "build": "rollup -c && node wrap.mjs", "prepack": "yarn build" diff --git a/src/web/module-loader/package.json b/src/web/module-loader/package.json index 9f2a563..50661ef 100644 --- a/src/web/module-loader/package.json +++ b/src/web/module-loader/package.json @@ -3,6 +3,11 @@ "version": "0.0.2", "packageManager": "yarn@4.12.0", "description": "The web plugin for the module loader in Source Academy", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/web/module-loader" + }, "scripts": { "build": "rollup -c", "prepack": "yarn build" diff --git a/src/web/stepper/package.json b/src/web/stepper/package.json index 99ef606..73eae4a 100644 --- a/src/web/stepper/package.json +++ b/src/web/stepper/package.json @@ -3,6 +3,11 @@ "version": "0.0.1", "packageManager": "yarn@4.12.0", "description": "Host (web) plugin for the Source Academy stepper: renders evaluation steps", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/web/stepper" + }, "scripts": { "build": "rollup -c && node wrap.mjs", "prepack": "yarn build" diff --git a/src/web/test/package.json b/src/web/test/package.json index dd07d98..bd61566 100644 --- a/src/web/test/package.json +++ b/src/web/test/package.json @@ -3,6 +3,11 @@ "version": "0.0.2", "packageManager": "yarn@4.12.0", "description": "The test web plugin for the plugins repository", + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/web/test" + }, "scripts": { "build": "rollup -c", "prepack": "yarn build" diff --git a/yarn.config.cjs b/yarn.config.cjs new file mode 100644 index 0000000..0e28320 --- /dev/null +++ b/yarn.config.cjs @@ -0,0 +1,22 @@ +// @ts-check + +const pathlib = require('path'); +const { defineConfig } = require('@yarnpkg/types'); + +module.exports = defineConfig({ + async constraints({ Yarn }) { + for (const workspace of Yarn.workspaces()) { + // Ignore the root workspace + if (workspace.cwd === '.') continue; + + const normedPath = workspace.cwd.split(pathlib.sep).join(pathlib.posix.sep); + + // Check that the repository field for each workspace is correct + workspace.set('repository', { + type: "git", + url: "git+https://github.com/source-academy/plugins", + directory: normedPath + }); + } + } +}); diff --git a/yarn.lock b/yarn.lock index 3ca6297..56c57cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2947,6 +2947,15 @@ __metadata: languageName: node linkType: hard +"@yarnpkg/types@npm:^4.0.1": + version: 4.0.1 + resolution: "@yarnpkg/types@npm:4.0.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/90226789475680ba599833571dd76c0718dd5b4c5022481263ef309d6a628f6246671cd6ca86e49c966ddefa7aca6ccef82240dc1476d2cea702ea5bee2a6b72 + languageName: node + linkType: hard + "abbrev@npm:^4.0.0": version: 4.0.0 resolution: "abbrev@npm:4.0.0" @@ -5947,6 +5956,7 @@ __metadata: "@sourceacademy/plugin-directory": "source-academy/plugin-directory#main" "@types/node": "npm:^25.9.1" "@vitest/coverage-istanbul": "npm:^4.1.9" + "@yarnpkg/types": "npm:^4.0.1" commander: "npm:^15.0.0" eslint: "npm:^10.0.3" eslint-config-prettier: "npm:^10.1.8" From b9af7c1aac7cd27d0156a93b4ace55d5152c452b Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Thu, 6 Aug 2026 19:52:58 +0800 Subject: [PATCH 02/29] Use yarn catalog for shared dependencies --- .gitignore | 2 +- .vscode/extensions.json | 6 +- .vscode/settings.json | 6 +- .yarn/sdks/eslint/bin/eslint.js | 32 ++ .yarn/sdks/eslint/lib/api.js | 32 ++ .yarn/sdks/eslint/lib/config-api.js | 32 ++ .yarn/sdks/eslint/lib/types/config-api.d.ts | 32 ++ .yarn/sdks/eslint/lib/types/index.d.ts | 32 ++ .yarn/sdks/eslint/lib/types/rules.d.ts | 32 ++ .yarn/sdks/eslint/lib/types/universal.d.ts | 32 ++ .../lib/types/use-at-your-own-risk.d.ts | 32 ++ .yarn/sdks/eslint/lib/universal.js | 32 ++ .yarn/sdks/eslint/lib/unsupported-api.js | 32 ++ .yarn/sdks/eslint/package.json | 31 ++ .yarn/sdks/prettier/bin/prettier.cjs | 32 ++ .yarn/sdks/prettier/index.cjs | 32 ++ .yarn/sdks/prettier/package.json | 7 + .yarn/sdks/typescript/lib/tsc.js | 8 +- .yarn/sdks/typescript/lib/tsserver.js | 177 +++----- .yarn/sdks/typescript/lib/tsserverlibrary.js | 177 +++----- .yarn/sdks/typescript/lib/typescript.js | 8 +- .yarnrc.yml | 13 +- package.json | 9 +- src/common/autocomplete/package.json | 19 +- src/common/cse-machine/package.json | 14 +- src/common/data-visualizer/package.json | 17 +- src/common/module-loader/package.json | 14 +- src/common/stepper/package.json | 12 +- src/common/tabs/package.json | 12 +- src/common/test/package.json | 14 +- src/runner/autocomplete/package.json | 23 +- src/runner/cse-machine/package.json | 18 +- src/runner/data-visualizer/package.json | 16 +- src/runner/module-loader/package.json | 18 +- src/runner/remoteExecution/package.json | 18 +- src/runner/stepper/package.json | 16 +- src/runner/test/package.json | 18 +- src/web/autocomplete/package.json | 25 +- src/web/cse-machine/package.json | 18 +- src/web/data-visualizer/package.json | 16 +- src/web/module-loader/package.json | 20 +- src/web/stepper/package.json | 16 +- src/web/test/package.json | 20 +- vitest.config.js => vitest.config.ts | 0 yarn.lock | 386 +++++++++--------- 45 files changed, 956 insertions(+), 602 deletions(-) create mode 100644 .yarn/sdks/eslint/bin/eslint.js create mode 100644 .yarn/sdks/eslint/lib/api.js create mode 100644 .yarn/sdks/eslint/lib/config-api.js create mode 100644 .yarn/sdks/eslint/lib/types/config-api.d.ts create mode 100644 .yarn/sdks/eslint/lib/types/index.d.ts create mode 100644 .yarn/sdks/eslint/lib/types/rules.d.ts create mode 100644 .yarn/sdks/eslint/lib/types/universal.d.ts create mode 100644 .yarn/sdks/eslint/lib/types/use-at-your-own-risk.d.ts create mode 100644 .yarn/sdks/eslint/lib/universal.js create mode 100644 .yarn/sdks/eslint/lib/unsupported-api.js create mode 100644 .yarn/sdks/eslint/package.json create mode 100644 .yarn/sdks/prettier/bin/prettier.cjs create mode 100644 .yarn/sdks/prettier/index.cjs create mode 100644 .yarn/sdks/prettier/package.json rename vitest.config.js => vitest.config.ts (100%) diff --git a/.gitignore b/.gitignore index f05b277..e7dc879 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,4 @@ dist coverage # Claude Code -CLAUDE.md \ No newline at end of file +CLAUDE.md diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 1453f7f..daaa5ee 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,7 @@ { - "recommendations": ["arcanis.vscode-zipfs"] + "recommendations": [ + "arcanis.vscode-zipfs", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode" + ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index a357ac5..53d8c03 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,8 @@ "**/.yarn": true, "**/.pnp.*": true }, - "js/ts.enablePromptUseWorkspaceTsdk": true, - "js/ts.tsdk.path": ".yarn/sdks/typescript/lib" + "js/ts.tsdk.promptToUseWorkspaceVersion": true, + "js/ts.tsdk.path": ".yarn/sdks/typescript/lib", + "eslint.nodePath": ".yarn/sdks", + "prettier.prettierPath": ".yarn/sdks/prettier/index.cjs", } diff --git a/.yarn/sdks/eslint/bin/eslint.js b/.yarn/sdks/eslint/bin/eslint.js new file mode 100644 index 0000000..e6604ff --- /dev/null +++ b/.yarn/sdks/eslint/bin/eslint.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/bin/eslint.js + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint/bin/eslint.js your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint/bin/eslint.js`)); diff --git a/.yarn/sdks/eslint/lib/api.js b/.yarn/sdks/eslint/lib/api.js new file mode 100644 index 0000000..8addf97 --- /dev/null +++ b/.yarn/sdks/eslint/lib/api.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint`)); diff --git a/.yarn/sdks/eslint/lib/config-api.js b/.yarn/sdks/eslint/lib/config-api.js new file mode 100644 index 0000000..e84435d --- /dev/null +++ b/.yarn/sdks/eslint/lib/config-api.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/config + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint/config your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint/config`)); diff --git a/.yarn/sdks/eslint/lib/types/config-api.d.ts b/.yarn/sdks/eslint/lib/types/config-api.d.ts new file mode 100644 index 0000000..174070b --- /dev/null +++ b/.yarn/sdks/eslint/lib/types/config-api.d.ts @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/config + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint/config your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint/config`)); diff --git a/.yarn/sdks/eslint/lib/types/index.d.ts b/.yarn/sdks/eslint/lib/types/index.d.ts new file mode 100644 index 0000000..19293d0 --- /dev/null +++ b/.yarn/sdks/eslint/lib/types/index.d.ts @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint`)); diff --git a/.yarn/sdks/eslint/lib/types/rules.d.ts b/.yarn/sdks/eslint/lib/types/rules.d.ts new file mode 100644 index 0000000..8d79c4c --- /dev/null +++ b/.yarn/sdks/eslint/lib/types/rules.d.ts @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/rules + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint/rules your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint/rules`)); diff --git a/.yarn/sdks/eslint/lib/types/universal.d.ts b/.yarn/sdks/eslint/lib/types/universal.d.ts new file mode 100644 index 0000000..662b3f4 --- /dev/null +++ b/.yarn/sdks/eslint/lib/types/universal.d.ts @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/universal + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint/universal your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint/universal`)); diff --git a/.yarn/sdks/eslint/lib/types/use-at-your-own-risk.d.ts b/.yarn/sdks/eslint/lib/types/use-at-your-own-risk.d.ts new file mode 100644 index 0000000..2e2ccca --- /dev/null +++ b/.yarn/sdks/eslint/lib/types/use-at-your-own-risk.d.ts @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/use-at-your-own-risk + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint/use-at-your-own-risk your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint/use-at-your-own-risk`)); diff --git a/.yarn/sdks/eslint/lib/universal.js b/.yarn/sdks/eslint/lib/universal.js new file mode 100644 index 0000000..85a8ccb --- /dev/null +++ b/.yarn/sdks/eslint/lib/universal.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/universal + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint/universal your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint/universal`)); diff --git a/.yarn/sdks/eslint/lib/unsupported-api.js b/.yarn/sdks/eslint/lib/unsupported-api.js new file mode 100644 index 0000000..c2b464c --- /dev/null +++ b/.yarn/sdks/eslint/lib/unsupported-api.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/use-at-your-own-risk + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint/use-at-your-own-risk your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint/use-at-your-own-risk`)); diff --git a/.yarn/sdks/eslint/package.json b/.yarn/sdks/eslint/package.json new file mode 100644 index 0000000..f26d3e7 --- /dev/null +++ b/.yarn/sdks/eslint/package.json @@ -0,0 +1,31 @@ +{ + "name": "eslint", + "version": "10.8.0-sdk", + "main": "./lib/api.js", + "type": "commonjs", + "bin": { + "eslint": "./bin/eslint.js" + }, + "exports": { + ".": { + "types": "./lib/types/index.d.ts", + "default": "./lib/api.js" + }, + "./config": { + "types": "./lib/types/config-api.d.ts", + "default": "./lib/config-api.js" + }, + "./package.json": "./package.json", + "./use-at-your-own-risk": { + "types": "./lib/types/use-at-your-own-risk.d.ts", + "default": "./lib/unsupported-api.js" + }, + "./rules": { + "types": "./lib/types/rules.d.ts" + }, + "./universal": { + "types": "./lib/types/universal.d.ts", + "default": "./lib/universal.js" + } + } +} diff --git a/.yarn/sdks/prettier/bin/prettier.cjs b/.yarn/sdks/prettier/bin/prettier.cjs new file mode 100644 index 0000000..9a4098f --- /dev/null +++ b/.yarn/sdks/prettier/bin/prettier.cjs @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require prettier/bin/prettier.cjs + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real prettier/bin/prettier.cjs your application uses +module.exports = wrapWithUserWrapper(absRequire(`prettier/bin/prettier.cjs`)); diff --git a/.yarn/sdks/prettier/index.cjs b/.yarn/sdks/prettier/index.cjs new file mode 100644 index 0000000..57cb2ab --- /dev/null +++ b/.yarn/sdks/prettier/index.cjs @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require prettier + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real prettier your application uses +module.exports = wrapWithUserWrapper(absRequire(`prettier`)); diff --git a/.yarn/sdks/prettier/package.json b/.yarn/sdks/prettier/package.json new file mode 100644 index 0000000..88ba091 --- /dev/null +++ b/.yarn/sdks/prettier/package.json @@ -0,0 +1,7 @@ +{ + "name": "prettier", + "version": "3.8.3-sdk", + "main": "./index.cjs", + "type": "commonjs", + "bin": "./bin/prettier.cjs" +} diff --git a/.yarn/sdks/typescript/lib/tsc.js b/.yarn/sdks/typescript/lib/tsc.js index 857b6f9..da411bd 100644 --- a/.yarn/sdks/typescript/lib/tsc.js +++ b/.yarn/sdks/typescript/lib/tsc.js @@ -1,9 +1,9 @@ #!/usr/bin/env node -const { existsSync } = require(`fs`); -const { createRequire, register } = require(`module`); -const { resolve } = require(`path`); -const { pathToFileURL } = require(`url`); +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); const relPnpApiPath = "../../../../.pnp.cjs"; diff --git a/.yarn/sdks/typescript/lib/tsserver.js b/.yarn/sdks/typescript/lib/tsserver.js index c266bc6..6249c46 100644 --- a/.yarn/sdks/typescript/lib/tsserver.js +++ b/.yarn/sdks/typescript/lib/tsserver.js @@ -1,9 +1,9 @@ #!/usr/bin/env node -const { existsSync } = require(`fs`); -const { createRequire, register } = require(`module`); -const { resolve } = require(`path`); -const { pathToFileURL } = require(`url`); +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); const relPnpApiPath = "../../../../.pnp.cjs"; @@ -37,18 +37,16 @@ const moduleWrapperFn = tsserver => { return tsserver; } - const { isAbsolute } = require(`path`); + const {isAbsolute} = require(`path`); const pnpApi = require(`pnpapi`); const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//); const isPortal = str => str.startsWith("portal:/"); const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`); - const dependencyTreeRoots = new Set( - pnpApi.getDependencyTreeRoots().map(locator => { - return `${locator.name}@${locator.reference}`; - }), - ); + const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => { + return `${locator.name}@${locator.reference}`; + })); // VSCode sends the zip paths to TS using the "zip://" prefix, that TS // doesn't understand. This layer makes sure to remove the protocol @@ -56,11 +54,7 @@ const moduleWrapperFn = tsserver => { function toEditorPath(str) { // We add the `zip:` prefix to both `.zip/` paths and virtual paths - if ( - isAbsolute(str) && - !str.match(/^\^?(zip:|\/zip\/)/) && - (str.match(/\.zip\//) || isVirtual(str)) - ) { + if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) { // We also take the opportunity to turn virtual paths into physical ones; // this makes it much easier to work with workspaces that list peer // dependencies, since otherwise Ctrl+Click would bring us to the virtual @@ -74,11 +68,7 @@ const moduleWrapperFn = tsserver => { const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str; if (resolved) { const locator = pnpApi.findPackageLocator(resolved); - if ( - locator && - (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || - isPortal(locator.reference)) - ) { + if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) { str = resolved; } } @@ -106,55 +96,41 @@ const moduleWrapperFn = tsserver => { // Before | ^/zip/c:/foo/bar.zip/package.json // After | ^/zip//c:/foo/bar.zip/package.json // - case `vscode <1.61`: - { - str = `^zip:${str}`; - } - break; + case `vscode <1.61`: { + str = `^zip:${str}`; + } break; - case `vscode <1.66`: - { - str = `^/zip/${str}`; - } - break; + case `vscode <1.66`: { + str = `^/zip/${str}`; + } break; - case `vscode <1.68`: - { - str = `^/zip${str}`; - } - break; + case `vscode <1.68`: { + str = `^/zip${str}`; + } break; - case `vscode`: - { - str = `^/zip/${str}`; - } - break; + case `vscode`: { + str = `^/zip/${str}`; + } break; // To make "go to definition" work, // We have to resolve the actual file system path from virtual path // and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip) - case `coc-nvim`: - { - str = normalize(resolved).replace(/\.zip\//, `.zip::`); - str = resolve(`zipfile:${str}`); - } - break; + case `coc-nvim`: { + str = normalize(resolved).replace(/\.zip\//, `.zip::`); + str = resolve(`zipfile:${str}`); + } break; // Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server) // We have to resolve the actual file system path from virtual path, // everything else is up to neovim - case `neovim`: - { - str = normalize(resolved).replace(/\.zip\//, `.zip::`); - str = `zipfile://${str}`; - } - break; - - default: - { - str = `zip:${str}`; - } - break; + case `neovim`: { + str = normalize(resolved).replace(/\.zip\//, `.zip::`); + str = `zipfile://${str}`; + } break; + + default: { + str = `zip:${str}`; + } break; } } else { str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`); @@ -166,35 +142,26 @@ const moduleWrapperFn = tsserver => { function fromEditorPath(str) { switch (hostInfo) { - case `coc-nvim`: - { - str = str.replace(/\.zip::/, `.zip/`); - // The path for coc-nvim is in format of //zipfile://.yarn/... - // So in order to convert it back, we use .* to match all the thing - // before `zipfile:` - return process.platform === `win32` - ? str.replace(/^.*zipfile:\//, ``) - : str.replace(/^.*zipfile:/, ``); - } - break; - - case `neovim`: - { - str = str.replace(/\.zip::/, `.zip/`); - // The path for neovim is in format of zipfile:////.yarn/... - return str.replace(/^zipfile:\/\//, ``); - } - break; + case `coc-nvim`: { + str = str.replace(/\.zip::/, `.zip/`); + // The path for coc-nvim is in format of //zipfile://.yarn/... + // So in order to convert it back, we use .* to match all the thing + // before `zipfile:` + return process.platform === `win32` + ? str.replace(/^.*zipfile:\//, ``) + : str.replace(/^.*zipfile:/, ``); + } break; + + case `neovim`: { + str = str.replace(/\.zip::/, `.zip/`); + // The path for neovim is in format of zipfile:////.yarn/... + return str.replace(/^zipfile:\/\//, ``); + } break; case `vscode`: - default: - { - return str.replace( - /^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, - process.platform === `win32` ? `` : `/`, - ); - } - break; + default: { + return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`) + } break; } } @@ -206,9 +173,8 @@ const moduleWrapperFn = tsserver => { // TypeScript already does local loads and if this code is running the user trusts the workspace // https://github.com/microsoft/vscode/issues/45856 const ConfiguredProject = tsserver.server.ConfiguredProject; - const { enablePluginsWithOptions: originalEnablePluginsWithOptions } = - ConfiguredProject.prototype; - ConfiguredProject.prototype.enablePluginsWithOptions = function () { + const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype; + ConfiguredProject.prototype.enablePluginsWithOptions = function() { this.projectService.allowLocalPluginLoads = true; return originalEnablePluginsWithOptions.apply(this, arguments); }; @@ -218,12 +184,12 @@ const moduleWrapperFn = tsserver => { // like an absolute path of ours and normalize it. const Session = tsserver.server.Session; - const { onMessage: originalOnMessage, send: originalSend } = Session.prototype; + const {onMessage: originalOnMessage, send: originalSend} = Session.prototype; let hostInfo = `unknown`; Object.assign(Session.prototype, { onMessage(/** @type {string | object} */ message) { - const isStringMessage = typeof message === "string"; + const isStringMessage = typeof message === 'string'; const parsedMessage = isStringMessage ? JSON.parse(message) : message; if ( @@ -234,12 +200,10 @@ const moduleWrapperFn = tsserver => { ) { hostInfo = parsedMessage.arguments.hostInfo; if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) { - const [, major, minor] = ( - process.env.VSCODE_IPC_HOOK.match( - // The RegExp from https://semver.org/ but without the caret at the start - /(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/, - ) ?? [] - ).map(Number); + const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match( + // The RegExp from https://semver.org/ but without the caret at the start + /(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/ + ) ?? []).map(Number) if (major === 1) { if (minor < 61) { @@ -254,33 +218,26 @@ const moduleWrapperFn = tsserver => { } const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => { - return typeof value === "string" ? fromEditorPath(value) : value; + return typeof value === 'string' ? fromEditorPath(value) : value; }); return originalOnMessage.call( this, - isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON), + isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON) ); }, send(/** @type {any} */ msg) { - return originalSend.call( - this, - JSON.parse( - JSON.stringify(msg, (key, value) => { - return typeof value === `string` ? toEditorPath(value) : value; - }), - ), - ); - }, + return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => { + return typeof value === `string` ? toEditorPath(value) : value; + }))); + } }); return tsserver; }; -const [major, minor] = absRequire(`typescript/package.json`) - .version.split(`.`, 2) - .map(value => parseInt(value, 10)); +const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10)); // In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well. // Ref https://github.com/microsoft/TypeScript/pull/55326 if (major > 5 || (major === 5 && minor >= 5)) { diff --git a/.yarn/sdks/typescript/lib/tsserverlibrary.js b/.yarn/sdks/typescript/lib/tsserverlibrary.js index 3072888..0e50e0a 100644 --- a/.yarn/sdks/typescript/lib/tsserverlibrary.js +++ b/.yarn/sdks/typescript/lib/tsserverlibrary.js @@ -1,9 +1,9 @@ #!/usr/bin/env node -const { existsSync } = require(`fs`); -const { createRequire, register } = require(`module`); -const { resolve } = require(`path`); -const { pathToFileURL } = require(`url`); +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); const relPnpApiPath = "../../../../.pnp.cjs"; @@ -37,18 +37,16 @@ const moduleWrapperFn = tsserver => { return tsserver; } - const { isAbsolute } = require(`path`); + const {isAbsolute} = require(`path`); const pnpApi = require(`pnpapi`); const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//); const isPortal = str => str.startsWith("portal:/"); const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`); - const dependencyTreeRoots = new Set( - pnpApi.getDependencyTreeRoots().map(locator => { - return `${locator.name}@${locator.reference}`; - }), - ); + const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => { + return `${locator.name}@${locator.reference}`; + })); // VSCode sends the zip paths to TS using the "zip://" prefix, that TS // doesn't understand. This layer makes sure to remove the protocol @@ -56,11 +54,7 @@ const moduleWrapperFn = tsserver => { function toEditorPath(str) { // We add the `zip:` prefix to both `.zip/` paths and virtual paths - if ( - isAbsolute(str) && - !str.match(/^\^?(zip:|\/zip\/)/) && - (str.match(/\.zip\//) || isVirtual(str)) - ) { + if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) { // We also take the opportunity to turn virtual paths into physical ones; // this makes it much easier to work with workspaces that list peer // dependencies, since otherwise Ctrl+Click would bring us to the virtual @@ -74,11 +68,7 @@ const moduleWrapperFn = tsserver => { const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str; if (resolved) { const locator = pnpApi.findPackageLocator(resolved); - if ( - locator && - (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || - isPortal(locator.reference)) - ) { + if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) { str = resolved; } } @@ -106,55 +96,41 @@ const moduleWrapperFn = tsserver => { // Before | ^/zip/c:/foo/bar.zip/package.json // After | ^/zip//c:/foo/bar.zip/package.json // - case `vscode <1.61`: - { - str = `^zip:${str}`; - } - break; + case `vscode <1.61`: { + str = `^zip:${str}`; + } break; - case `vscode <1.66`: - { - str = `^/zip/${str}`; - } - break; + case `vscode <1.66`: { + str = `^/zip/${str}`; + } break; - case `vscode <1.68`: - { - str = `^/zip${str}`; - } - break; + case `vscode <1.68`: { + str = `^/zip${str}`; + } break; - case `vscode`: - { - str = `^/zip/${str}`; - } - break; + case `vscode`: { + str = `^/zip/${str}`; + } break; // To make "go to definition" work, // We have to resolve the actual file system path from virtual path // and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip) - case `coc-nvim`: - { - str = normalize(resolved).replace(/\.zip\//, `.zip::`); - str = resolve(`zipfile:${str}`); - } - break; + case `coc-nvim`: { + str = normalize(resolved).replace(/\.zip\//, `.zip::`); + str = resolve(`zipfile:${str}`); + } break; // Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server) // We have to resolve the actual file system path from virtual path, // everything else is up to neovim - case `neovim`: - { - str = normalize(resolved).replace(/\.zip\//, `.zip::`); - str = `zipfile://${str}`; - } - break; - - default: - { - str = `zip:${str}`; - } - break; + case `neovim`: { + str = normalize(resolved).replace(/\.zip\//, `.zip::`); + str = `zipfile://${str}`; + } break; + + default: { + str = `zip:${str}`; + } break; } } else { str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`); @@ -166,35 +142,26 @@ const moduleWrapperFn = tsserver => { function fromEditorPath(str) { switch (hostInfo) { - case `coc-nvim`: - { - str = str.replace(/\.zip::/, `.zip/`); - // The path for coc-nvim is in format of //zipfile://.yarn/... - // So in order to convert it back, we use .* to match all the thing - // before `zipfile:` - return process.platform === `win32` - ? str.replace(/^.*zipfile:\//, ``) - : str.replace(/^.*zipfile:/, ``); - } - break; - - case `neovim`: - { - str = str.replace(/\.zip::/, `.zip/`); - // The path for neovim is in format of zipfile:////.yarn/... - return str.replace(/^zipfile:\/\//, ``); - } - break; + case `coc-nvim`: { + str = str.replace(/\.zip::/, `.zip/`); + // The path for coc-nvim is in format of //zipfile://.yarn/... + // So in order to convert it back, we use .* to match all the thing + // before `zipfile:` + return process.platform === `win32` + ? str.replace(/^.*zipfile:\//, ``) + : str.replace(/^.*zipfile:/, ``); + } break; + + case `neovim`: { + str = str.replace(/\.zip::/, `.zip/`); + // The path for neovim is in format of zipfile:////.yarn/... + return str.replace(/^zipfile:\/\//, ``); + } break; case `vscode`: - default: - { - return str.replace( - /^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, - process.platform === `win32` ? `` : `/`, - ); - } - break; + default: { + return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`) + } break; } } @@ -206,9 +173,8 @@ const moduleWrapperFn = tsserver => { // TypeScript already does local loads and if this code is running the user trusts the workspace // https://github.com/microsoft/vscode/issues/45856 const ConfiguredProject = tsserver.server.ConfiguredProject; - const { enablePluginsWithOptions: originalEnablePluginsWithOptions } = - ConfiguredProject.prototype; - ConfiguredProject.prototype.enablePluginsWithOptions = function () { + const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype; + ConfiguredProject.prototype.enablePluginsWithOptions = function() { this.projectService.allowLocalPluginLoads = true; return originalEnablePluginsWithOptions.apply(this, arguments); }; @@ -218,12 +184,12 @@ const moduleWrapperFn = tsserver => { // like an absolute path of ours and normalize it. const Session = tsserver.server.Session; - const { onMessage: originalOnMessage, send: originalSend } = Session.prototype; + const {onMessage: originalOnMessage, send: originalSend} = Session.prototype; let hostInfo = `unknown`; Object.assign(Session.prototype, { onMessage(/** @type {string | object} */ message) { - const isStringMessage = typeof message === "string"; + const isStringMessage = typeof message === 'string'; const parsedMessage = isStringMessage ? JSON.parse(message) : message; if ( @@ -234,12 +200,10 @@ const moduleWrapperFn = tsserver => { ) { hostInfo = parsedMessage.arguments.hostInfo; if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) { - const [, major, minor] = ( - process.env.VSCODE_IPC_HOOK.match( - // The RegExp from https://semver.org/ but without the caret at the start - /(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/, - ) ?? [] - ).map(Number); + const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match( + // The RegExp from https://semver.org/ but without the caret at the start + /(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/ + ) ?? []).map(Number) if (major === 1) { if (minor < 61) { @@ -254,33 +218,26 @@ const moduleWrapperFn = tsserver => { } const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => { - return typeof value === "string" ? fromEditorPath(value) : value; + return typeof value === 'string' ? fromEditorPath(value) : value; }); return originalOnMessage.call( this, - isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON), + isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON) ); }, send(/** @type {any} */ msg) { - return originalSend.call( - this, - JSON.parse( - JSON.stringify(msg, (key, value) => { - return typeof value === `string` ? toEditorPath(value) : value; - }), - ), - ); - }, + return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => { + return typeof value === `string` ? toEditorPath(value) : value; + }))); + } }); return tsserver; }; -const [major, minor] = absRequire(`typescript/package.json`) - .version.split(`.`, 2) - .map(value => parseInt(value, 10)); +const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10)); // In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well. // Ref https://github.com/microsoft/TypeScript/pull/55326 if (major > 5 || (major === 5 && minor >= 5)) { diff --git a/.yarn/sdks/typescript/lib/typescript.js b/.yarn/sdks/typescript/lib/typescript.js index bc1c8f7..7b6cc22 100644 --- a/.yarn/sdks/typescript/lib/typescript.js +++ b/.yarn/sdks/typescript/lib/typescript.js @@ -1,9 +1,9 @@ #!/usr/bin/env node -const { existsSync } = require(`fs`); -const { createRequire, register } = require(`module`); -const { resolve } = require(`path`); -const { pathToFileURL } = require(`url`); +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); const relPnpApiPath = "../../../../.pnp.cjs"; diff --git a/.yarnrc.yml b/.yarnrc.yml index 1a226b4..e22e804 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1 +1,12 @@ -npmPreapprovedPackages: ["@sourceacademy/conductor"] \ No newline at end of file +npmPreapprovedPackages: ["@sourceacademy/conductor"] + +catalog: + rollup: ^4.60.2 + "@rollup/plugin-commonjs": ^29.0.3 + "@rollup/plugin-node-resolve": ^16.0.3 + "@rollup/plugin-terser": ^1.0.0 + "@rollup/plugin-typescript": ^12.3.0 + "@sourceacademy/conductor": ^0.8.0 + tslib: ^2.8.1 + typescript: ^6.0.3 + vitest: ^4.1.9 diff --git a/package.json b/package.json index 3da5c2e..ab5e898 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { - "name": "plugins", + "name": "@sourceacademy/plugins-workspace", + "private": true, "packageManager": "yarn@4.18.0", "workspaces": [ "src/common/*/", @@ -19,7 +20,7 @@ }, "devDependencies": { "@changesets/cli": "^2.31.0", - "@sourceacademy/conductor": "^0.8.0", + "@sourceacademy/conductor": "catalog:", "@sourceacademy/plugin-directory": "source-academy/plugin-directory#main", "@types/node": "^25.9.1", "@vitest/coverage-istanbul": "^4.1.9", @@ -29,9 +30,9 @@ "eslint-config-prettier": "^10.1.8", "prettier": "^3.8.1", "tsx": "^4.22.3", - "typescript": "^6.0.3", + "typescript": "catalog:", "typescript-eslint": "^8.57.1", - "vitest": "^4.1.9" + "vitest": "catalog:" }, "repository": { "type": "git", diff --git a/src/common/autocomplete/package.json b/src/common/autocomplete/package.json index e8afd12..5e31088 100644 --- a/src/common/autocomplete/package.json +++ b/src/common/autocomplete/package.json @@ -22,12 +22,17 @@ } }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3", - "vitest": "^4.1.9" + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/common/autocomplete" } } diff --git a/src/common/cse-machine/package.json b/src/common/cse-machine/package.json index a4af2a7..1a76709 100644 --- a/src/common/cse-machine/package.json +++ b/src/common/cse-machine/package.json @@ -30,13 +30,13 @@ } }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@vitest/coverage-istanbul": "^4.1.9", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3", - "vitest": "^4.1.9" + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/src/common/data-visualizer/package.json b/src/common/data-visualizer/package.json index 6de8e76..8a890ec 100644 --- a/src/common/data-visualizer/package.json +++ b/src/common/data-visualizer/package.json @@ -24,14 +24,19 @@ } }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@types/jest": "^30.0.0", "jest": "^30.4.2", - "rollup": "^4.60.2", + "rollup": "catalog:", "ts-jest": "^29.4.11", - "tslib": "^2.8.1", - "typescript": "^6.0.3" + "tslib": "catalog:", + "typescript": "catalog:" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/common/data-visualizer" } } diff --git a/src/common/module-loader/package.json b/src/common/module-loader/package.json index 957cc21..8872f67 100644 --- a/src/common/module-loader/package.json +++ b/src/common/module-loader/package.json @@ -27,12 +27,12 @@ } }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3", - "vitest": "^4.1.9" + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/src/common/stepper/package.json b/src/common/stepper/package.json index 1e7abc5..77886c5 100644 --- a/src/common/stepper/package.json +++ b/src/common/stepper/package.json @@ -29,14 +29,14 @@ } }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@types/jest": "^30.0.0", "jest": "^30.4.2", - "rollup": "^4.60.2", + "rollup": "catalog:", "ts-jest": "^29.4.11", - "tslib": "^2.8.1", - "typescript": "^6.0.3" + "tslib": "catalog:", + "typescript": "catalog:" } } diff --git a/src/common/tabs/package.json b/src/common/tabs/package.json index a0d7dfa..52e18f8 100644 --- a/src/common/tabs/package.json +++ b/src/common/tabs/package.json @@ -32,12 +32,12 @@ }, "devDependencies": { "@blueprintjs/icons": "^6.0.0", - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@types/react": "^19.2.17", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3" + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:" } } diff --git a/src/common/test/package.json b/src/common/test/package.json index fd62005..87bca3d 100644 --- a/src/common/test/package.json +++ b/src/common/test/package.json @@ -27,12 +27,12 @@ } }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3", - "vitest": "^4.1.9" + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/src/runner/autocomplete/package.json b/src/runner/autocomplete/package.json index 1f71926..0ca2f50 100644 --- a/src/runner/autocomplete/package.json +++ b/src/runner/autocomplete/package.json @@ -25,19 +25,24 @@ } }, "peerDependencies": { - "@sourceacademy/conductor": "^0.8.0" + "@sourceacademy/conductor": "*" }, "dependencies": { "@sourceacademy/common-autocomplete": "^0.0.1" }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", - "@sourceacademy/conductor": "^0.8.0", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3", - "vitest": "^4.1.9" + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", + "@sourceacademy/conductor": "catalog:", + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/runner/autocomplete" } } diff --git a/src/runner/cse-machine/package.json b/src/runner/cse-machine/package.json index f837683..65beadf 100644 --- a/src/runner/cse-machine/package.json +++ b/src/runner/cse-machine/package.json @@ -31,18 +31,18 @@ }, "peerDependencies": { "@sourceacademy/common-cse-machine": ">=0.3.0", - "@sourceacademy/conductor": "^0.8.0" + "@sourceacademy/conductor": "*" }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@sourceacademy/common-cse-machine": "workspace:*", - "@sourceacademy/conductor": "^0.8.0", + "@sourceacademy/conductor": "catalog:", "@vitest/coverage-istanbul": "^4.1.9", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3", - "vitest": "^4.1.9" + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/src/runner/data-visualizer/package.json b/src/runner/data-visualizer/package.json index 64c3fec..6148429 100644 --- a/src/runner/data-visualizer/package.json +++ b/src/runner/data-visualizer/package.json @@ -29,19 +29,19 @@ } }, "peerDependencies": { - "@sourceacademy/conductor": "^0.8.0" + "@sourceacademy/conductor": "*" }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@sourceacademy/common-data-visualizer": "workspace:*", - "@sourceacademy/conductor": "^0.8.0", + "@sourceacademy/conductor": "catalog:", "@types/jest": "^30.0.0", "jest": "^30.4.2", - "rollup": "^4.60.2", + "rollup": "catalog:", "ts-jest": "^29.4.11", - "tslib": "^2.8.1", - "typescript": "^6.0.3" + "tslib": "catalog:", + "typescript": "catalog:" } } diff --git a/src/runner/module-loader/package.json b/src/runner/module-loader/package.json index 4574837..5232d42 100644 --- a/src/runner/module-loader/package.json +++ b/src/runner/module-loader/package.json @@ -27,19 +27,19 @@ } }, "peerDependencies": { - "@sourceacademy/conductor": "^0.8.0" + "@sourceacademy/conductor": "*" }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@sourceacademy/common-module-loader": "workspace:*", - "@sourceacademy/conductor": "^0.8.0", + "@sourceacademy/conductor": "catalog:", "@sourceacademy/runner-remote-execution": "workspace:*", "@vitest/coverage-istanbul": "^4.1.9", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3", - "vitest": "^4.1.9" + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/src/runner/remoteExecution/package.json b/src/runner/remoteExecution/package.json index 05ef589..35118d7 100644 --- a/src/runner/remoteExecution/package.json +++ b/src/runner/remoteExecution/package.json @@ -31,18 +31,18 @@ "py-slang": "github:proto-aiken-13/py-slang#branch-ev3-engine" }, "peerDependencies": { - "@sourceacademy/conductor": "^0.8.0" + "@sourceacademy/conductor": "*" }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@sourceacademy/common-test": "workspace:*", - "@sourceacademy/conductor": "^0.8.0", + "@sourceacademy/conductor": "catalog:", "@vitest/coverage-istanbul": "^4.1.9", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3", - "vitest": "^4.1.9" + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/src/runner/stepper/package.json b/src/runner/stepper/package.json index c4edd76..4deb8d8 100644 --- a/src/runner/stepper/package.json +++ b/src/runner/stepper/package.json @@ -29,19 +29,19 @@ } }, "peerDependencies": { - "@sourceacademy/conductor": "^0.8.0" + "@sourceacademy/conductor": "*" }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@sourceacademy/common-stepper": "workspace:*", - "@sourceacademy/conductor": "^0.8.0", + "@sourceacademy/conductor": "catalog:", "@types/jest": "^30.0.0", "jest": "^30.4.2", - "rollup": "^4.60.2", + "rollup": "catalog:", "ts-jest": "^29.4.11", - "tslib": "^2.8.1", - "typescript": "^6.0.3" + "tslib": "catalog:", + "typescript": "catalog:" } } diff --git a/src/runner/test/package.json b/src/runner/test/package.json index 4873d2a..ce0220c 100644 --- a/src/runner/test/package.json +++ b/src/runner/test/package.json @@ -27,20 +27,20 @@ } }, "peerDependencies": { - "@sourceacademy/conductor": "^0.8.0" + "@sourceacademy/conductor": "*" }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@sourceacademy/common-test": "workspace:*", - "@sourceacademy/conductor": "^0.8.0", + "@sourceacademy/conductor": "catalog:", "@sourceacademy/runner-remote-execution": "workspace:*", "@vitest/coverage-istanbul": "^4.1.9", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3", - "vitest": "^4.1.9" + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" }, "dependencies": { "py-slang": "github:proto-aiken-13/py-slang#branch-ev3-engine" diff --git a/src/web/autocomplete/package.json b/src/web/autocomplete/package.json index cfccd8f..eff8c53 100644 --- a/src/web/autocomplete/package.json +++ b/src/web/autocomplete/package.json @@ -25,20 +25,25 @@ } }, "peerDependencies": { - "@sourceacademy/conductor": "^0.8.0" + "@sourceacademy/conductor": "*" }, "dependencies": { "@sourceacademy/common-autocomplete": "^0.0.1" }, "devDependencies": { - "@rollup/plugin-commonjs": "^29.0.3", - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", - "@sourceacademy/conductor": "^0.8.0", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3", - "vitest": "^4.1.9" + "@rollup/plugin-commonjs": "catalog:", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", + "@sourceacademy/conductor": "catalog:", + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/source-academy/plugins", + "directory": "src/web/autocomplete" } } diff --git a/src/web/cse-machine/package.json b/src/web/cse-machine/package.json index cc8bd03..8f48f1e 100644 --- a/src/web/cse-machine/package.json +++ b/src/web/cse-machine/package.json @@ -31,18 +31,18 @@ }, "peerDependencies": { "@sourceacademy/common-cse-machine": ">=0.3.0", - "@sourceacademy/conductor": "^0.8.0" + "@sourceacademy/conductor": "*" }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@sourceacademy/common-cse-machine": "workspace:*", - "@sourceacademy/conductor": "^0.8.0", + "@sourceacademy/conductor": "catalog:", "@vitest/coverage-istanbul": "^4.1.9", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3", - "vitest": "^4.1.9" + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/src/web/data-visualizer/package.json b/src/web/data-visualizer/package.json index 8274ed2..6ad699e 100644 --- a/src/web/data-visualizer/package.json +++ b/src/web/data-visualizer/package.json @@ -33,14 +33,14 @@ "devDependencies": { "@blueprintjs/core": "^6.0.0", "@blueprintjs/icons": "^6.0.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-node-resolve": "^16.0.3", + "@rollup/plugin-commonjs": "catalog:", + "@rollup/plugin-node-resolve": "catalog:", "@rollup/plugin-replace": "^6.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@sourceacademy/common-data-visualizer": "workspace:*", "@sourceacademy/common-tabs": "workspace:^", - "@sourceacademy/conductor": "^0.8.0", + "@sourceacademy/conductor": "catalog:", "@types/react": "^19.0.0", "@types/react-test-renderer": "^19.0.0", "konva": "^9.3.0", @@ -48,8 +48,8 @@ "react-dom": "^19.0.0", "react-konva": "^19.0.0", "react-test-renderer": "^19.0.0", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3" + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:" } } diff --git a/src/web/module-loader/package.json b/src/web/module-loader/package.json index b926c82..2b6eb28 100644 --- a/src/web/module-loader/package.json +++ b/src/web/module-loader/package.json @@ -30,19 +30,19 @@ } }, "peerDependencies": { - "@sourceacademy/conductor": "^0.8.0" + "@sourceacademy/conductor": "*" }, "devDependencies": { - "@rollup/plugin-commonjs": "^29.0.3", - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-commonjs": "catalog:", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@sourceacademy/common-module-loader": "workspace:*", "@sourceacademy/common-tabs": "workspace:^", - "@sourceacademy/conductor": "^0.8.0", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3", - "vitest": "^4.1.9" + "@sourceacademy/conductor": "catalog:", + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/src/web/stepper/package.json b/src/web/stepper/package.json index 77f5f66..a06ba11 100644 --- a/src/web/stepper/package.json +++ b/src/web/stepper/package.json @@ -32,19 +32,19 @@ "@blueprintjs/core": "^6.0.0", "@blueprintjs/icons": "^6.0.0", "@mantine/hooks": "^9.0.0", - "@rollup/plugin-commonjs": "^28.0.0", - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-commonjs": "catalog:", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@sourceacademy/common-stepper": "workspace:*", "@sourceacademy/common-tabs": "workspace:^", - "@sourceacademy/conductor": "^0.8.0", + "@sourceacademy/conductor": "catalog:", "@types/react": "^19.0.0", "classnames": "^2.3.2", "react": "^19.0.0", "react-dom": "^19.0.0", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3" + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:" } } diff --git a/src/web/test/package.json b/src/web/test/package.json index f97d877..abeeb5f 100644 --- a/src/web/test/package.json +++ b/src/web/test/package.json @@ -31,22 +31,22 @@ }, "peerDependencies": { "@sourceacademy/common-tabs": "^0.0.1", - "@sourceacademy/conductor": "^0.8.0", + "@sourceacademy/conductor": "*", "react": "^19", "react-dom": "^19" }, "devDependencies": { - "@rollup/plugin-commonjs": "^29.0.3", - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-commonjs": "catalog:", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@sourceacademy/common-tabs": "workspace:^", "@sourceacademy/common-test": "workspace:*", - "@sourceacademy/conductor": "^0.8.0", + "@sourceacademy/conductor": "catalog:", "@types/react": "^19.2.17", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3", - "vitest": "^4.1.9" + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/vitest.config.js b/vitest.config.ts similarity index 100% rename from vitest.config.js rename to vitest.config.ts diff --git a/yarn.lock b/yarn.lock index 6423ca6..c0e983c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1637,26 +1637,6 @@ __metadata: languageName: node linkType: hard -"@rollup/plugin-commonjs@npm:^28.0.0": - version: 28.0.9 - resolution: "@rollup/plugin-commonjs@npm:28.0.9" - dependencies: - "@rollup/pluginutils": "npm:^5.0.1" - commondir: "npm:^1.0.1" - estree-walker: "npm:^2.0.2" - fdir: "npm:^6.2.0" - is-reference: "npm:1.2.1" - magic-string: "npm:^0.30.3" - picomatch: "npm:^4.0.2" - peerDependencies: - rollup: ^2.68.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - checksum: 10c0/b7af70614a53c549a1ba1e9647879b644bcf44ec78850f04018b929f4ee414274f867fa438308409a06ef8a3a179ed24c25e4f8ef77eb341dfddd2b0cb88c389 - languageName: node - linkType: hard - "@rollup/plugin-commonjs@npm:^29.0.3": version: 29.0.3 resolution: "@rollup/plugin-commonjs@npm:29.0.3" @@ -1965,13 +1945,13 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/common-autocomplete@workspace:src/common/autocomplete" dependencies: - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" - vitest: "npm:^4.1.9" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" + vitest: "catalog:" languageName: unknown linkType: soft @@ -1979,14 +1959,14 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/common-cse-machine@workspace:src/common/cse-machine" dependencies: - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@vitest/coverage-istanbul": "npm:^4.1.9" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" - vitest: "npm:^4.1.9" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" + vitest: "catalog:" languageName: unknown linkType: soft @@ -1994,15 +1974,15 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/common-data-visualizer@workspace:src/common/data-visualizer" dependencies: - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@types/jest": "npm:^30.0.0" jest: "npm:^30.4.2" - rollup: "npm:^4.60.2" + rollup: "catalog:" ts-jest: "npm:^29.4.11" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" + tslib: "catalog:" + typescript: "catalog:" languageName: unknown linkType: soft @@ -2010,13 +1990,13 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/common-module-loader@workspace:src/common/module-loader" dependencies: - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" - vitest: "npm:^4.1.9" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" + vitest: "catalog:" languageName: unknown linkType: soft @@ -2024,15 +2004,15 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/common-stepper@workspace:src/common/stepper" dependencies: - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@types/jest": "npm:^30.0.0" jest: "npm:^30.4.2" - rollup: "npm:^4.60.2" + rollup: "catalog:" ts-jest: "npm:^29.4.11" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" + tslib: "catalog:" + typescript: "catalog:" languageName: unknown linkType: soft @@ -2041,13 +2021,13 @@ __metadata: resolution: "@sourceacademy/common-tabs@workspace:src/common/tabs" dependencies: "@blueprintjs/icons": "npm:^6.0.0" - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@types/react": "npm:^19.2.17" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" peerDependencies: "@blueprintjs/icons": ^6.0.0 react: ^18 || ^19 @@ -2058,13 +2038,13 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/common-test@workspace:src/common/test" dependencies: - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" - vitest: "npm:^4.1.9" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" + vitest: "catalog:" languageName: unknown linkType: soft @@ -2089,21 +2069,42 @@ __metadata: languageName: node linkType: hard +"@sourceacademy/plugins-workspace@workspace:.": + version: 0.0.0-use.local + resolution: "@sourceacademy/plugins-workspace@workspace:." + dependencies: + "@changesets/cli": "npm:^2.31.0" + "@sourceacademy/conductor": "catalog:" + "@sourceacademy/plugin-directory": "source-academy/plugin-directory#main" + "@types/node": "npm:^25.9.1" + "@vitest/coverage-istanbul": "npm:^4.1.9" + "@yarnpkg/types": "npm:^4.0.1" + commander: "npm:^15.0.0" + eslint: "npm:^10.0.3" + eslint-config-prettier: "npm:^10.1.8" + prettier: "npm:^3.8.1" + tsx: "npm:^4.22.3" + typescript: "catalog:" + typescript-eslint: "npm:^8.57.1" + vitest: "catalog:" + languageName: unknown + linkType: soft + "@sourceacademy/runner-autocomplete@workspace:src/runner/autocomplete": version: 0.0.0-use.local resolution: "@sourceacademy/runner-autocomplete@workspace:src/runner/autocomplete" dependencies: - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-autocomplete": "npm:^0.0.1" - "@sourceacademy/conductor": "npm:^0.8.0" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" - vitest: "npm:^4.1.9" + "@sourceacademy/conductor": "catalog:" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" + vitest: "catalog:" peerDependencies: - "@sourceacademy/conductor": ^0.8.0 + "@sourceacademy/conductor": "*" languageName: unknown linkType: soft @@ -2111,19 +2112,19 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/runner-cse-machine@workspace:src/runner/cse-machine" dependencies: - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-cse-machine": "workspace:*" - "@sourceacademy/conductor": "npm:^0.8.0" + "@sourceacademy/conductor": "catalog:" "@vitest/coverage-istanbul": "npm:^4.1.9" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" - vitest: "npm:^4.1.9" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" + vitest: "catalog:" peerDependencies: "@sourceacademy/common-cse-machine": ">=0.3.0" - "@sourceacademy/conductor": ^0.8.0 + "@sourceacademy/conductor": "*" languageName: unknown linkType: soft @@ -2131,19 +2132,19 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/runner-data-visualizer@workspace:src/runner/data-visualizer" dependencies: - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-data-visualizer": "workspace:*" - "@sourceacademy/conductor": "npm:^0.8.0" + "@sourceacademy/conductor": "catalog:" "@types/jest": "npm:^30.0.0" jest: "npm:^30.4.2" - rollup: "npm:^4.60.2" + rollup: "catalog:" ts-jest: "npm:^29.4.11" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" + tslib: "catalog:" + typescript: "catalog:" peerDependencies: - "@sourceacademy/conductor": ^0.8.0 + "@sourceacademy/conductor": "*" languageName: unknown linkType: soft @@ -2151,19 +2152,19 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/runner-module-loader@workspace:src/runner/module-loader" dependencies: - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-module-loader": "workspace:*" - "@sourceacademy/conductor": "npm:^0.8.0" + "@sourceacademy/conductor": "catalog:" "@sourceacademy/runner-remote-execution": "workspace:*" "@vitest/coverage-istanbul": "npm:^4.1.9" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" - vitest: "npm:^4.1.9" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" + vitest: "catalog:" peerDependencies: - "@sourceacademy/conductor": ^0.8.0 + "@sourceacademy/conductor": "*" languageName: unknown linkType: soft @@ -2171,19 +2172,19 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/runner-remote-execution@workspace:src/runner/remoteExecution" dependencies: - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-test": "workspace:*" - "@sourceacademy/conductor": "npm:^0.8.0" + "@sourceacademy/conductor": "catalog:" "@vitest/coverage-istanbul": "npm:^4.1.9" py-slang: "github:proto-aiken-13/py-slang#branch-ev3-engine" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" - vitest: "npm:^4.1.9" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" + vitest: "catalog:" peerDependencies: - "@sourceacademy/conductor": ^0.8.0 + "@sourceacademy/conductor": "*" languageName: unknown linkType: soft @@ -2191,19 +2192,19 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/runner-stepper@workspace:src/runner/stepper" dependencies: - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-stepper": "workspace:*" - "@sourceacademy/conductor": "npm:^0.8.0" + "@sourceacademy/conductor": "catalog:" "@types/jest": "npm:^30.0.0" jest: "npm:^30.4.2" - rollup: "npm:^4.60.2" + rollup: "catalog:" ts-jest: "npm:^29.4.11" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" + tslib: "catalog:" + typescript: "catalog:" peerDependencies: - "@sourceacademy/conductor": ^0.8.0 + "@sourceacademy/conductor": "*" languageName: unknown linkType: soft @@ -2211,20 +2212,20 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/runner-test@workspace:src/runner/test" dependencies: - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-test": "workspace:*" - "@sourceacademy/conductor": "npm:^0.8.0" + "@sourceacademy/conductor": "catalog:" "@sourceacademy/runner-remote-execution": "workspace:*" "@vitest/coverage-istanbul": "npm:^4.1.9" py-slang: "github:proto-aiken-13/py-slang#branch-ev3-engine" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" - vitest: "npm:^4.1.9" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" + vitest: "catalog:" peerDependencies: - "@sourceacademy/conductor": ^0.8.0 + "@sourceacademy/conductor": "*" languageName: unknown linkType: soft @@ -2239,18 +2240,18 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/web-autocomplete@workspace:src/web/autocomplete" dependencies: - "@rollup/plugin-commonjs": "npm:^29.0.3" - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-commonjs": "catalog:" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-autocomplete": "npm:^0.0.1" - "@sourceacademy/conductor": "npm:^0.8.0" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" - vitest: "npm:^4.1.9" + "@sourceacademy/conductor": "catalog:" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" + vitest: "catalog:" peerDependencies: - "@sourceacademy/conductor": ^0.8.0 + "@sourceacademy/conductor": "*" languageName: unknown linkType: soft @@ -2258,19 +2259,19 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/web-cse-machine@workspace:src/web/cse-machine" dependencies: - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-cse-machine": "workspace:*" - "@sourceacademy/conductor": "npm:^0.8.0" + "@sourceacademy/conductor": "catalog:" "@vitest/coverage-istanbul": "npm:^4.1.9" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" - vitest: "npm:^4.1.9" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" + vitest: "catalog:" peerDependencies: "@sourceacademy/common-cse-machine": ">=0.3.0" - "@sourceacademy/conductor": ^0.8.0 + "@sourceacademy/conductor": "*" languageName: unknown linkType: soft @@ -2280,14 +2281,14 @@ __metadata: dependencies: "@blueprintjs/core": "npm:^6.0.0" "@blueprintjs/icons": "npm:^6.0.0" - "@rollup/plugin-commonjs": "npm:^28.0.0" - "@rollup/plugin-node-resolve": "npm:^16.0.3" + "@rollup/plugin-commonjs": "catalog:" + "@rollup/plugin-node-resolve": "catalog:" "@rollup/plugin-replace": "npm:^6.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-data-visualizer": "workspace:*" "@sourceacademy/common-tabs": "workspace:^" - "@sourceacademy/conductor": "npm:^0.8.0" + "@sourceacademy/conductor": "catalog:" "@types/react": "npm:^19.0.0" "@types/react-test-renderer": "npm:^19.0.0" konva: "npm:^9.3.0" @@ -2295,9 +2296,9 @@ __metadata: react-dom: "npm:^19.0.0" react-konva: "npm:^19.0.0" react-test-renderer: "npm:^19.0.0" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" peerDependencies: "@blueprintjs/core": ">=5" konva: ">=9" @@ -2311,19 +2312,19 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/web-module-loader@workspace:src/web/module-loader" dependencies: - "@rollup/plugin-commonjs": "npm:^29.0.3" - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-commonjs": "catalog:" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-module-loader": "workspace:*" "@sourceacademy/common-tabs": "workspace:^" - "@sourceacademy/conductor": "npm:^0.8.0" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" - vitest: "npm:^4.1.9" + "@sourceacademy/conductor": "catalog:" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" + vitest: "catalog:" peerDependencies: - "@sourceacademy/conductor": ^0.8.0 + "@sourceacademy/conductor": "*" languageName: unknown linkType: soft @@ -2334,20 +2335,20 @@ __metadata: "@blueprintjs/core": "npm:^6.0.0" "@blueprintjs/icons": "npm:^6.0.0" "@mantine/hooks": "npm:^9.0.0" - "@rollup/plugin-commonjs": "npm:^28.0.0" - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-commonjs": "catalog:" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-stepper": "workspace:*" "@sourceacademy/common-tabs": "workspace:^" - "@sourceacademy/conductor": "npm:^0.8.0" + "@sourceacademy/conductor": "catalog:" "@types/react": "npm:^19.0.0" classnames: "npm:^2.3.2" react: "npm:^19.0.0" react-dom: "npm:^19.0.0" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" peerDependencies: "@blueprintjs/core": ">=5" react: ">=18" @@ -2359,21 +2360,21 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/web-test@workspace:src/web/test" dependencies: - "@rollup/plugin-commonjs": "npm:^29.0.3" - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-commonjs": "catalog:" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-tabs": "workspace:^" "@sourceacademy/common-test": "workspace:*" - "@sourceacademy/conductor": "npm:^0.8.0" + "@sourceacademy/conductor": "catalog:" "@types/react": "npm:^19.2.17" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" - vitest: "npm:^4.1.9" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" + vitest: "catalog:" peerDependencies: "@sourceacademy/common-tabs": ^0.0.1 - "@sourceacademy/conductor": ^0.8.0 + "@sourceacademy/conductor": "*" react: ^19 react-dom: ^19 languageName: unknown @@ -6009,27 +6010,6 @@ __metadata: languageName: node linkType: hard -"plugins@workspace:.": - version: 0.0.0-use.local - resolution: "plugins@workspace:." - dependencies: - "@changesets/cli": "npm:^2.31.0" - "@sourceacademy/conductor": "npm:^0.8.0" - "@sourceacademy/plugin-directory": "source-academy/plugin-directory#main" - "@types/node": "npm:^25.9.1" - "@vitest/coverage-istanbul": "npm:^4.1.9" - "@yarnpkg/types": "npm:^4.0.1" - commander: "npm:^15.0.0" - eslint: "npm:^10.0.3" - eslint-config-prettier: "npm:^10.1.8" - prettier: "npm:^3.8.1" - tsx: "npm:^4.22.3" - typescript: "npm:^6.0.3" - typescript-eslint: "npm:^8.57.1" - vitest: "npm:^4.1.9" - languageName: unknown - linkType: soft - "postcss@npm:^8.5.15": version: 8.5.23 resolution: "postcss@npm:8.5.23" From c531692b8054df2515e2462920c25d1a962a67c6 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Thu, 6 Aug 2026 20:44:38 +0800 Subject: [PATCH 03/29] Use rollup defineConfig helper and add ts-check for rollup config files --- eslint.config.mjs | 7 +++++-- src/common/autocomplete/rollup.config.mjs | 10 +++++----- src/common/cse-machine/rollup.config.mjs | 11 ++++++----- src/common/data-visualizer/rollup.config.mjs | 10 +++++----- src/common/module-loader/rollup.config.mjs | 11 ++++++----- src/common/stepper/rollup.config.mjs | 10 +++++----- src/common/tabs/rollup.config.mjs | 11 ++++++----- src/common/test/rollup.config.mjs | 10 +++++----- src/runner/autocomplete/rollup.config.mjs | 11 ++++++----- src/runner/cse-machine/rollup.config.mjs | 10 +++++----- src/runner/data-visualizer/rollup.config.mjs | 11 ++++++----- src/runner/module-loader/rollup.config.mjs | 8 +++++--- src/runner/remoteExecution/rollup.config.mjs | 10 +++++----- src/runner/stepper/rollup.config.mjs | 11 ++++++----- src/runner/test/rollup.config.mjs | 8 +++++--- src/web/autocomplete/rollup.config.mjs | 11 ++++++----- src/web/cse-machine/rollup.config.mjs | 10 +++++----- src/web/data-visualizer/rollup.config.mjs | 10 +++++----- src/web/module-loader/rollup.config.mjs | 11 ++++++----- src/web/stepper/rollup.config.mjs | 10 +++++----- src/web/test/rollup.config.mjs | 12 +++++++----- 21 files changed, 115 insertions(+), 98 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index cfae433..d0051ff 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,7 +4,7 @@ import tseslint from "typescript-eslint"; export default defineConfig([ { - ignores: ["**/dist", "coverage"], + ignores: ["**/dist", "**/node_modules", "coverage"], }, tseslint.configs.recommended, eslintConfigPrettierFlat, @@ -13,7 +13,9 @@ export default defineConfig([ languageOptions: { parser: tseslint.parser, parserOptions: { - project: ["src/tsconfig.json", "src/tsconfig.test.json"], + projectService: { + allowDefaultProject: ['vitest.config.ts'] + }, tsconfigRootDir: import.meta.dirname, }, }, @@ -26,6 +28,7 @@ export default defineConfig([ "@typescript-eslint/no-import-type-side-effects": "error", "@typescript-eslint/no-unnecessary-type-assertion": "error", "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }], + '@typescript-eslint/only-throw-error': 'error', "@typescript-eslint/require-await": "error", "@typescript-eslint/return-await": ["error", "in-try-catch"], }, diff --git a/src/common/autocomplete/rollup.config.mjs b/src/common/autocomplete/rollup.config.mjs index 238a07c..0d387b7 100644 --- a/src/common/autocomplete/rollup.config.mjs +++ b/src/common/autocomplete/rollup.config.mjs @@ -1,11 +1,11 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: [ { @@ -18,4 +18,4 @@ export default { }, ], plugins: [nodeResolve(), typescript(), terser()], -}; +}); diff --git a/src/common/cse-machine/rollup.config.mjs b/src/common/cse-machine/rollup.config.mjs index 238a07c..58838a5 100644 --- a/src/common/cse-machine/rollup.config.mjs +++ b/src/common/cse-machine/rollup.config.mjs @@ -1,11 +1,11 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: [ { @@ -18,4 +18,5 @@ export default { }, ], plugins: [nodeResolve(), typescript(), terser()], -}; +}); + diff --git a/src/common/data-visualizer/rollup.config.mjs b/src/common/data-visualizer/rollup.config.mjs index 238a07c..0d387b7 100644 --- a/src/common/data-visualizer/rollup.config.mjs +++ b/src/common/data-visualizer/rollup.config.mjs @@ -1,11 +1,11 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: [ { @@ -18,4 +18,4 @@ export default { }, ], plugins: [nodeResolve(), typescript(), terser()], -}; +}); diff --git a/src/common/module-loader/rollup.config.mjs b/src/common/module-loader/rollup.config.mjs index 238a07c..58838a5 100644 --- a/src/common/module-loader/rollup.config.mjs +++ b/src/common/module-loader/rollup.config.mjs @@ -1,11 +1,11 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: [ { @@ -18,4 +18,5 @@ export default { }, ], plugins: [nodeResolve(), typescript(), terser()], -}; +}); + diff --git a/src/common/stepper/rollup.config.mjs b/src/common/stepper/rollup.config.mjs index 238a07c..0d387b7 100644 --- a/src/common/stepper/rollup.config.mjs +++ b/src/common/stepper/rollup.config.mjs @@ -1,11 +1,11 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: [ { @@ -18,4 +18,4 @@ export default { }, ], plugins: [nodeResolve(), typescript(), terser()], -}; +}); diff --git a/src/common/tabs/rollup.config.mjs b/src/common/tabs/rollup.config.mjs index 238a07c..58838a5 100644 --- a/src/common/tabs/rollup.config.mjs +++ b/src/common/tabs/rollup.config.mjs @@ -1,11 +1,11 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: [ { @@ -18,4 +18,5 @@ export default { }, ], plugins: [nodeResolve(), typescript(), terser()], -}; +}); + diff --git a/src/common/test/rollup.config.mjs b/src/common/test/rollup.config.mjs index 238a07c..0d387b7 100644 --- a/src/common/test/rollup.config.mjs +++ b/src/common/test/rollup.config.mjs @@ -1,11 +1,11 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: [ { @@ -18,4 +18,4 @@ export default { }, ], plugins: [nodeResolve(), typescript(), terser()], -}; +}); diff --git a/src/runner/autocomplete/rollup.config.mjs b/src/runner/autocomplete/rollup.config.mjs index e633c7e..244d88e 100644 --- a/src/runner/autocomplete/rollup.config.mjs +++ b/src/runner/autocomplete/rollup.config.mjs @@ -1,11 +1,11 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: [ { @@ -19,4 +19,5 @@ export default { ], plugins: [nodeResolve(), typescript(), terser()], external: [/^@sourceacademy\/conductor(?:\/|$)/], -}; +}); + diff --git a/src/runner/cse-machine/rollup.config.mjs b/src/runner/cse-machine/rollup.config.mjs index 238a07c..0d387b7 100644 --- a/src/runner/cse-machine/rollup.config.mjs +++ b/src/runner/cse-machine/rollup.config.mjs @@ -1,11 +1,11 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: [ { @@ -18,4 +18,4 @@ export default { }, ], plugins: [nodeResolve(), typescript(), terser()], -}; +}); diff --git a/src/runner/data-visualizer/rollup.config.mjs b/src/runner/data-visualizer/rollup.config.mjs index 238a07c..58838a5 100644 --- a/src/runner/data-visualizer/rollup.config.mjs +++ b/src/runner/data-visualizer/rollup.config.mjs @@ -1,11 +1,11 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: [ { @@ -18,4 +18,5 @@ export default { }, ], plugins: [nodeResolve(), typescript(), terser()], -}; +}); + diff --git a/src/runner/module-loader/rollup.config.mjs b/src/runner/module-loader/rollup.config.mjs index 3f56b30..0d387b7 100644 --- a/src/runner/module-loader/rollup.config.mjs +++ b/src/runner/module-loader/rollup.config.mjs @@ -1,9 +1,11 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; -/** @type {import('rollup').RollupOptions} */ -export default { +export default defineConfig({ input: "src/index.ts", output: [ { @@ -16,4 +18,4 @@ export default { }, ], plugins: [nodeResolve(), typescript(), terser()], -}; +}); diff --git a/src/runner/remoteExecution/rollup.config.mjs b/src/runner/remoteExecution/rollup.config.mjs index 75b8ba5..0284287 100644 --- a/src/runner/remoteExecution/rollup.config.mjs +++ b/src/runner/remoteExecution/rollup.config.mjs @@ -1,11 +1,11 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", external: id => id.includes("py-slang"), output: [ @@ -19,4 +19,4 @@ export default { }, ], plugins: [nodeResolve(), typescript(), terser()], -}; +}); diff --git a/src/runner/stepper/rollup.config.mjs b/src/runner/stepper/rollup.config.mjs index 238a07c..58838a5 100644 --- a/src/runner/stepper/rollup.config.mjs +++ b/src/runner/stepper/rollup.config.mjs @@ -1,11 +1,11 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: [ { @@ -18,4 +18,5 @@ export default { }, ], plugins: [nodeResolve(), typescript(), terser()], -}; +}); + diff --git a/src/runner/test/rollup.config.mjs b/src/runner/test/rollup.config.mjs index 3f56b30..0d387b7 100644 --- a/src/runner/test/rollup.config.mjs +++ b/src/runner/test/rollup.config.mjs @@ -1,9 +1,11 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; -/** @type {import('rollup').RollupOptions} */ -export default { +export default defineConfig({ input: "src/index.ts", output: [ { @@ -16,4 +18,4 @@ export default { }, ], plugins: [nodeResolve(), typescript(), terser()], -}; +}); diff --git a/src/web/autocomplete/rollup.config.mjs b/src/web/autocomplete/rollup.config.mjs index c326a2f..6e487db 100644 --- a/src/web/autocomplete/rollup.config.mjs +++ b/src/web/autocomplete/rollup.config.mjs @@ -1,11 +1,12 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; import commonjs from "@rollup/plugin-commonjs"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { + +export default defineConfig({ input: "src/index.ts", output: [ { @@ -19,4 +20,4 @@ export default { ], plugins: [nodeResolve(), commonjs(), typescript(), terser()], external: ["react", "react-dom", "react/jsx-runtime", /^@sourceacademy\/conductor(?:\/|$)/], -}; +}); diff --git a/src/web/cse-machine/rollup.config.mjs b/src/web/cse-machine/rollup.config.mjs index 238a07c..0d387b7 100644 --- a/src/web/cse-machine/rollup.config.mjs +++ b/src/web/cse-machine/rollup.config.mjs @@ -1,11 +1,11 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: [ { @@ -18,4 +18,4 @@ export default { }, ], plugins: [nodeResolve(), typescript(), terser()], -}; +}); diff --git a/src/web/data-visualizer/rollup.config.mjs b/src/web/data-visualizer/rollup.config.mjs index 1a20d0a..6ea81f3 100644 --- a/src/web/data-visualizer/rollup.config.mjs +++ b/src/web/data-visualizer/rollup.config.mjs @@ -1,3 +1,6 @@ +// @ts-check + +import { defineConfig } from "rollup"; import commonjs from "@rollup/plugin-commonjs"; import nodeResolve from "@rollup/plugin-node-resolve"; import replace from "@rollup/plugin-replace"; @@ -21,10 +24,7 @@ const external = [ "react-konva", ]; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: { file: "dist/index.cjs", @@ -41,4 +41,4 @@ export default { typescript(), terser(), ], -}; +}); diff --git a/src/web/module-loader/rollup.config.mjs b/src/web/module-loader/rollup.config.mjs index 06a3173..b8570c2 100644 --- a/src/web/module-loader/rollup.config.mjs +++ b/src/web/module-loader/rollup.config.mjs @@ -1,11 +1,12 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; import commonjs from "@rollup/plugin-commonjs"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { + +export default defineConfig({ input: "src/index.ts", output: [ { @@ -19,4 +20,4 @@ export default { ], plugins: [nodeResolve(), commonjs(), typescript(), terser()], external: ["react", "react-dom", "react/jsx-runtime"], -}; +}); diff --git a/src/web/stepper/rollup.config.mjs b/src/web/stepper/rollup.config.mjs index 2ec3d1d..95a3702 100644 --- a/src/web/stepper/rollup.config.mjs +++ b/src/web/stepper/rollup.config.mjs @@ -1,3 +1,6 @@ +// @ts-check + +import { defineConfig } from "rollup"; import commonjs from "@rollup/plugin-commonjs"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; @@ -15,10 +18,7 @@ const external = [ "@blueprintjs/core", ]; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: { file: "dist/index.cjs", @@ -32,4 +32,4 @@ export default { typescript(), terser(), ], -}; +}); diff --git a/src/web/test/rollup.config.mjs b/src/web/test/rollup.config.mjs index 02b9dde..ebd33ea 100644 --- a/src/web/test/rollup.config.mjs +++ b/src/web/test/rollup.config.mjs @@ -1,11 +1,12 @@ +// @ts-check + +import { defineConfig } from "rollup"; import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; import commonjs from "@rollup/plugin-commonjs"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { + +export default defineConfig({ input: "src/index.tsx", output: [ { @@ -19,4 +20,5 @@ export default { ], plugins: [nodeResolve(), commonjs(), typescript(), terser()], external: ["react", "react-dom", "react/jsx-runtime"], -}; +}); + From 35920bbb96c4e03ae7a5c7c30be79c98ca94cc4c Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Thu, 6 Aug 2026 20:51:55 +0800 Subject: [PATCH 04/29] Remove jest --- src/common/data-visualizer/jest.config.cjs | 14 - src/common/data-visualizer/package.json | 7 +- src/common/data-visualizer/tsconfig.json | 1 - src/common/module-loader/package.json | 3 +- src/common/stepper/package.json | 10 +- src/common/stepper/tsconfig.json | 1 - src/runner/data-visualizer/jest.config.cjs | 14 - src/runner/data-visualizer/package.json | 10 +- src/runner/data-visualizer/tsconfig.json | 3 +- src/runner/stepper/jest.config.cjs | 14 - src/runner/stepper/package.json | 6 +- src/runner/stepper/tsconfig.json | 1 - yarn.lock | 2517 +------------------- 13 files changed, 53 insertions(+), 2548 deletions(-) delete mode 100644 src/common/data-visualizer/jest.config.cjs delete mode 100644 src/runner/data-visualizer/jest.config.cjs delete mode 100644 src/runner/stepper/jest.config.cjs diff --git a/src/common/data-visualizer/jest.config.cjs b/src/common/data-visualizer/jest.config.cjs deleted file mode 100644 index 06bb4a9..0000000 --- a/src/common/data-visualizer/jest.config.cjs +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - preset: "ts-jest/presets/js-with-ts-esm", - testEnvironment: "node", - transform: { - "^.+\\.tsx?$": [ - "ts-jest", - { - useESM: true, - }, - ], - }, - testPathIgnorePatterns: [".*?dist/"], - coverageReporters: ["lcov"], -}; diff --git a/src/common/data-visualizer/package.json b/src/common/data-visualizer/package.json index 8a890ec..4cc72f1 100644 --- a/src/common/data-visualizer/package.json +++ b/src/common/data-visualizer/package.json @@ -6,8 +6,8 @@ "scripts": { "build": "rollup -c", "prepack": "yarn build", - "test": "jest", - "test-coverage": "jest --coverage" + "test": "vitest", + "test-coverage": "vitest --coverage" }, "license": "ISC", "files": [ @@ -27,10 +27,7 @@ "@rollup/plugin-node-resolve": "catalog:", "@rollup/plugin-terser": "catalog:", "@rollup/plugin-typescript": "catalog:", - "@types/jest": "^30.0.0", - "jest": "^30.4.2", "rollup": "catalog:", - "ts-jest": "^29.4.11", "tslib": "catalog:", "typescript": "catalog:" }, diff --git a/src/common/data-visualizer/tsconfig.json b/src/common/data-visualizer/tsconfig.json index 88a8445..b71f4ee 100644 --- a/src/common/data-visualizer/tsconfig.json +++ b/src/common/data-visualizer/tsconfig.json @@ -6,6 +6,5 @@ "declaration": true, "outDir": "./dist", "rootDir": "./src", - "types": ["jest"] } } diff --git a/src/common/module-loader/package.json b/src/common/module-loader/package.json index 8872f67..0c78c7b 100644 --- a/src/common/module-loader/package.json +++ b/src/common/module-loader/package.json @@ -32,7 +32,6 @@ "@rollup/plugin-typescript": "catalog:", "rollup": "catalog:", "tslib": "catalog:", - "typescript": "catalog:", - "vitest": "catalog:" + "typescript": "catalog:" } } diff --git a/src/common/stepper/package.json b/src/common/stepper/package.json index 77886c5..c449836 100644 --- a/src/common/stepper/package.json +++ b/src/common/stepper/package.json @@ -11,8 +11,8 @@ "scripts": { "build": "rollup -c", "prepack": "yarn build", - "test": "jest", - "test-coverage": "jest --coverage" + "test": "vitest", + "test-coverage": "vitest --coverage" }, "license": "ISC", "files": [ @@ -32,11 +32,9 @@ "@rollup/plugin-node-resolve": "catalog:", "@rollup/plugin-terser": "catalog:", "@rollup/plugin-typescript": "catalog:", - "@types/jest": "^30.0.0", - "jest": "^30.4.2", "rollup": "catalog:", - "ts-jest": "^29.4.11", "tslib": "catalog:", - "typescript": "catalog:" + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/src/common/stepper/tsconfig.json b/src/common/stepper/tsconfig.json index 88a8445..b71f4ee 100644 --- a/src/common/stepper/tsconfig.json +++ b/src/common/stepper/tsconfig.json @@ -6,6 +6,5 @@ "declaration": true, "outDir": "./dist", "rootDir": "./src", - "types": ["jest"] } } diff --git a/src/runner/data-visualizer/jest.config.cjs b/src/runner/data-visualizer/jest.config.cjs deleted file mode 100644 index 06bb4a9..0000000 --- a/src/runner/data-visualizer/jest.config.cjs +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - preset: "ts-jest/presets/js-with-ts-esm", - testEnvironment: "node", - transform: { - "^.+\\.tsx?$": [ - "ts-jest", - { - useESM: true, - }, - ], - }, - testPathIgnorePatterns: [".*?dist/"], - coverageReporters: ["lcov"], -}; diff --git a/src/runner/data-visualizer/package.json b/src/runner/data-visualizer/package.json index 6148429..d7d1b68 100644 --- a/src/runner/data-visualizer/package.json +++ b/src/runner/data-visualizer/package.json @@ -11,8 +11,8 @@ "scripts": { "build": "rollup -c", "prepack": "yarn build", - "test": "jest", - "test-coverage": "jest --coverage" + "test": "vitest", + "test-coverage": "vitest --coverage" }, "license": "ISC", "files": [ @@ -37,11 +37,9 @@ "@rollup/plugin-typescript": "catalog:", "@sourceacademy/common-data-visualizer": "workspace:*", "@sourceacademy/conductor": "catalog:", - "@types/jest": "^30.0.0", - "jest": "^30.4.2", "rollup": "catalog:", - "ts-jest": "^29.4.11", "tslib": "catalog:", - "typescript": "catalog:" + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/src/runner/data-visualizer/tsconfig.json b/src/runner/data-visualizer/tsconfig.json index 88a8445..c7482f9 100644 --- a/src/runner/data-visualizer/tsconfig.json +++ b/src/runner/data-visualizer/tsconfig.json @@ -5,7 +5,6 @@ "compilerOptions": { "declaration": true, "outDir": "./dist", - "rootDir": "./src", - "types": ["jest"] + "rootDir": "./src" } } diff --git a/src/runner/stepper/jest.config.cjs b/src/runner/stepper/jest.config.cjs deleted file mode 100644 index 06bb4a9..0000000 --- a/src/runner/stepper/jest.config.cjs +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - preset: "ts-jest/presets/js-with-ts-esm", - testEnvironment: "node", - transform: { - "^.+\\.tsx?$": [ - "ts-jest", - { - useESM: true, - }, - ], - }, - testPathIgnorePatterns: [".*?dist/"], - coverageReporters: ["lcov"], -}; diff --git a/src/runner/stepper/package.json b/src/runner/stepper/package.json index 4deb8d8..b47b69d 100644 --- a/src/runner/stepper/package.json +++ b/src/runner/stepper/package.json @@ -37,11 +37,9 @@ "@rollup/plugin-typescript": "catalog:", "@sourceacademy/common-stepper": "workspace:*", "@sourceacademy/conductor": "catalog:", - "@types/jest": "^30.0.0", - "jest": "^30.4.2", "rollup": "catalog:", - "ts-jest": "^29.4.11", "tslib": "catalog:", - "typescript": "catalog:" + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/src/runner/stepper/tsconfig.json b/src/runner/stepper/tsconfig.json index 88a8445..b71f4ee 100644 --- a/src/runner/stepper/tsconfig.json +++ b/src/runner/stepper/tsconfig.json @@ -6,6 +6,5 @@ "declaration": true, "outDir": "./dist", "rootDir": "./src", - "types": ["jest"] } } diff --git a/yarn.lock b/yarn.lock index c0e983c..dbd1e86 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,7 +5,7 @@ __metadata: version: 10 cacheKey: 10c0 -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.27.1, @babel/code-frame@npm:^7.29.7": +"@babel/code-frame@npm:^7.29.7": version: 7.29.7 resolution: "@babel/code-frame@npm:7.29.7" dependencies: @@ -23,7 +23,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.23.9, @babel/core@npm:^7.27.4, @babel/core@npm:^7.29.0": +"@babel/core@npm:^7.29.0": version: 7.29.7 resolution: "@babel/core@npm:7.29.7" dependencies: @@ -46,7 +46,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.27.5, @babel/generator@npm:^7.29.7": +"@babel/generator@npm:^7.29.7": version: 7.29.7 resolution: "@babel/generator@npm:7.29.7" dependencies: @@ -102,13 +102,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.29.7, @babel/helper-plugin-utils@npm:^7.8.0": - version: 7.29.7 - resolution: "@babel/helper-plugin-utils@npm:7.29.7" - checksum: 10c0/380477a06133274a2759f9355929cb60a95e8b8fee624a1ae1fa349e1d1645b89daca456f72833f6d1062bffa12ee4271c5bf0cc5a61c0166cdc24c7591e2408 - languageName: node - linkType: hard - "@babel/helper-string-parser@npm:^7.29.7": version: 7.29.7 resolution: "@babel/helper-string-parser@npm:7.29.7" @@ -140,7 +133,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.29.3, @babel/parser@npm:^7.29.7": +"@babel/parser@npm:^7.29.3, @babel/parser@npm:^7.29.7": version: 7.29.7 resolution: "@babel/parser@npm:7.29.7" dependencies: @@ -151,193 +144,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-async-generators@npm:^7.8.4": - version: 7.8.4 - resolution: "@babel/plugin-syntax-async-generators@npm:7.8.4" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/d13efb282838481348c71073b6be6245b35d4f2f964a8f71e4174f235009f929ef7613df25f8d2338e2d3e44bc4265a9f8638c6aaa136d7a61fe95985f9725c8 - languageName: node - linkType: hard - -"@babel/plugin-syntax-bigint@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-bigint@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/686891b81af2bc74c39013655da368a480f17dd237bf9fbc32048e5865cb706d5a8f65438030da535b332b1d6b22feba336da8fa931f663b6b34e13147d12dde - languageName: node - linkType: hard - -"@babel/plugin-syntax-class-properties@npm:^7.12.13": - version: 7.12.13 - resolution: "@babel/plugin-syntax-class-properties@npm:7.12.13" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.12.13" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/95168fa186416195280b1264fb18afcdcdcea780b3515537b766cb90de6ce042d42dd6a204a39002f794ae5845b02afb0fd4861a3308a861204a55e68310a120 - languageName: node - linkType: hard - -"@babel/plugin-syntax-class-static-block@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/plugin-syntax-class-static-block@npm:7.14.5" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.14.5" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/4464bf9115f4a2d02ce1454411baf9cfb665af1da53709c5c56953e5e2913745b0fcce82982a00463d6facbdd93445c691024e310b91431a1e2f024b158f6371 - languageName: node - linkType: hard - -"@babel/plugin-syntax-import-attributes@npm:^7.24.7": - version: 7.29.7 - resolution: "@babel/plugin-syntax-import-attributes@npm:7.29.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.29.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/b9a47e869d8c06676297069895ae34e2bd244ec4c3bdf15002f1e69aed32eef0361044af22a43f271b8de5e23a40534fe6a74a63e7ab98e3d60a74b322444b49 - languageName: node - linkType: hard - -"@babel/plugin-syntax-import-meta@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/plugin-syntax-import-meta@npm:7.10.4" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.10.4" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/0b08b5e4c3128523d8e346f8cfc86824f0da2697b1be12d71af50a31aff7a56ceb873ed28779121051475010c28d6146a6bfea8518b150b71eeb4e46190172ee - languageName: node - linkType: hard - -"@babel/plugin-syntax-json-strings@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-json-strings@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/e98f31b2ec406c57757d115aac81d0336e8434101c224edd9a5c93cefa53faf63eacc69f3138960c8b25401315af03df37f68d316c151c4b933136716ed6906e - languageName: node - linkType: hard - -"@babel/plugin-syntax-jsx@npm:^7.27.1": - version: 7.29.7 - resolution: "@babel/plugin-syntax-jsx@npm:7.29.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.29.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/1736000de183538ba8eef34520105508860e48b0c763254ba9158af5e814ed8bbceeedbb4281fbda33de787ae5b3870e92f60c6ae7131e7d322e451d57387896 - languageName: node - linkType: hard - -"@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.10.4" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/2594cfbe29411ad5bc2ad4058de7b2f6a8c5b86eda525a993959438615479e59c012c14aec979e538d60a584a1a799b60d1b8942c3b18468cb9d99b8fd34cd0b - languageName: node - linkType: hard - -"@babel/plugin-syntax-nullish-coalescing-operator@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-nullish-coalescing-operator@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/2024fbb1162899094cfc81152449b12bd0cc7053c6d4bda8ac2852545c87d0a851b1b72ed9560673cbf3ef6248257262c3c04aabf73117215c1b9cc7dd2542ce - languageName: node - linkType: hard - -"@babel/plugin-syntax-numeric-separator@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/plugin-syntax-numeric-separator@npm:7.10.4" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.10.4" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/c55a82b3113480942c6aa2fcbe976ff9caa74b7b1109ff4369641dfbc88d1da348aceb3c31b6ed311c84d1e7c479440b961906c735d0ab494f688bf2fd5b9bb9 - languageName: node - linkType: hard - -"@babel/plugin-syntax-object-rest-spread@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-object-rest-spread@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/ee1eab52ea6437e3101a0a7018b0da698545230015fc8ab129d292980ec6dff94d265e9e90070e8ae5fed42f08f1622c14c94552c77bcac784b37f503a82ff26 - languageName: node - linkType: hard - -"@babel/plugin-syntax-optional-catch-binding@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-optional-catch-binding@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/27e2493ab67a8ea6d693af1287f7e9acec206d1213ff107a928e85e173741e1d594196f99fec50e9dde404b09164f39dec5864c767212154ffe1caa6af0bc5af - languageName: node - linkType: hard - -"@babel/plugin-syntax-optional-chaining@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-optional-chaining@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/46edddf2faa6ebf94147b8e8540dfc60a5ab718e2de4d01b2c0bdf250a4d642c2bd47cbcbb739febcb2bf75514dbcefad3c52208787994b8d0f8822490f55e81 - languageName: node - linkType: hard - -"@babel/plugin-syntax-private-property-in-object@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/plugin-syntax-private-property-in-object@npm:7.14.5" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.14.5" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/69822772561706c87f0a65bc92d0772cea74d6bc0911537904a676d5ff496a6d3ac4e05a166d8125fce4a16605bace141afc3611074e170a994e66e5397787f3 - languageName: node - linkType: hard - -"@babel/plugin-syntax-top-level-await@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/plugin-syntax-top-level-await@npm:7.14.5" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.14.5" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/14bf6e65d5bc1231ffa9def5f0ef30b19b51c218fcecaa78cd1bdf7939dfdf23f90336080b7f5196916368e399934ce5d581492d8292b46a2fb569d8b2da106f - languageName: node - linkType: hard - -"@babel/plugin-syntax-typescript@npm:^7.27.1": - version: 7.29.7 - resolution: "@babel/plugin-syntax-typescript@npm:7.29.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.29.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/c49883b0327e8683b770dc823205af5c697da216e590dcf5bf53f3f031e7e381de450b164f8f99853f0837a3de5cb793298e2be6697a0f6e452bb9dd34b5165e - languageName: node - linkType: hard - "@babel/runtime@npm:^7.26.10, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.7": version: 7.29.7 resolution: "@babel/runtime@npm:7.29.7" @@ -371,7 +177,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.29.0, @babel/types@npm:^7.29.7": +"@babel/types@npm:^7.29.0, @babel/types@npm:^7.29.7": version: 7.29.7 resolution: "@babel/types@npm:7.29.7" dependencies: @@ -381,13 +187,6 @@ __metadata: languageName: node linkType: hard -"@bcoe/v8-coverage@npm:^0.2.3": - version: 0.2.3 - resolution: "@bcoe/v8-coverage@npm:0.2.3" - checksum: 10c0/6b80ae4cb3db53f486da2dc63b6e190a74c8c3cca16bb2733f234a0b6a9382b09b146488ae08e2b22cf00f6c83e20f3e040a2f7894f05c045c946d6a090b1d52 - languageName: node - linkType: hard - "@blueprintjs/colors@npm:^5.1.16": version: 5.1.16 resolution: "@blueprintjs/colors@npm:5.1.16" @@ -1057,20 +856,6 @@ __metadata: languageName: node linkType: hard -"@isaacs/cliui@npm:^8.0.2": - version: 8.0.2 - resolution: "@isaacs/cliui@npm:8.0.2" - dependencies: - string-width: "npm:^5.1.2" - string-width-cjs: "npm:string-width@^4.2.0" - strip-ansi: "npm:^7.0.1" - strip-ansi-cjs: "npm:strip-ansi@^6.0.1" - wrap-ansi: "npm:^8.1.0" - wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" - checksum: 10c0/b1bf42535d49f11dc137f18d5e4e63a28c5569de438a221c369483731e9dac9fb797af554e8bf02b6192d1e5eba6e6402cf93900c3d0ac86391d00d04876789e - languageName: node - linkType: hard - "@isaacs/fs-minipass@npm:^4.0.0": version: 4.0.1 resolution: "@isaacs/fs-minipass@npm:4.0.1" @@ -1080,291 +865,13 @@ __metadata: languageName: node linkType: hard -"@istanbuljs/load-nyc-config@npm:^1.0.0": - version: 1.1.0 - resolution: "@istanbuljs/load-nyc-config@npm:1.1.0" - dependencies: - camelcase: "npm:^5.3.1" - find-up: "npm:^4.1.0" - get-package-type: "npm:^0.1.0" - js-yaml: "npm:^3.13.1" - resolve-from: "npm:^5.0.0" - checksum: 10c0/dd2a8b094887da5a1a2339543a4933d06db2e63cbbc2e288eb6431bd832065df0c099d091b6a67436e71b7d6bf85f01ce7c15f9253b4cbebcc3b9a496165ba42 - languageName: node - linkType: hard - -"@istanbuljs/schema@npm:^0.1.2, @istanbuljs/schema@npm:^0.1.3": +"@istanbuljs/schema@npm:^0.1.3": version: 0.1.6 resolution: "@istanbuljs/schema@npm:0.1.6" checksum: 10c0/bb0d370bf3dd454d2f37f1bccb8921e2da99adacef2da56ef47850e25d7a4de69cf639ead8c189755aef38921369024b4afea3535a5c2ac9082b3e1171bcbc3a languageName: node linkType: hard -"@jest/console@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/console@npm:30.4.1" - dependencies: - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - chalk: "npm:^4.1.2" - jest-message-util: "npm:30.4.1" - jest-util: "npm:30.4.1" - slash: "npm:^3.0.0" - checksum: 10c0/f782722ef5754ab864b996000cf1f0545f7be9db6ba8f89cb2381dfab9910a52c59a830e5ea069a76840023e40806493d9900d8eb7e9821d23a11a498f32739e - languageName: node - linkType: hard - -"@jest/core@npm:30.4.2": - version: 30.4.2 - resolution: "@jest/core@npm:30.4.2" - dependencies: - "@jest/console": "npm:30.4.1" - "@jest/pattern": "npm:30.4.0" - "@jest/reporters": "npm:30.4.1" - "@jest/test-result": "npm:30.4.1" - "@jest/transform": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - ansi-escapes: "npm:^4.3.2" - chalk: "npm:^4.1.2" - ci-info: "npm:^4.2.0" - exit-x: "npm:^0.2.2" - fast-json-stable-stringify: "npm:^2.1.0" - graceful-fs: "npm:^4.2.11" - jest-changed-files: "npm:30.4.1" - jest-config: "npm:30.4.2" - jest-haste-map: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-regex-util: "npm:30.4.0" - jest-resolve: "npm:30.4.1" - jest-resolve-dependencies: "npm:30.4.2" - jest-runner: "npm:30.4.2" - jest-runtime: "npm:30.4.2" - jest-snapshot: "npm:30.4.1" - jest-util: "npm:30.4.1" - jest-validate: "npm:30.4.1" - jest-watcher: "npm:30.4.1" - pretty-format: "npm:30.4.1" - slash: "npm:^3.0.0" - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - checksum: 10c0/4237ec79d5403b82ba89e3be6e4318d9f37c3a11281bd76cfbdd4ff08d8c89850555607c4d494dab3526e01a90db3539e549017883967dd392b5084f1be0d5b2 - languageName: node - linkType: hard - -"@jest/diff-sequences@npm:30.4.0": - version: 30.4.0 - resolution: "@jest/diff-sequences@npm:30.4.0" - checksum: 10c0/b4358b1b885098b905cb777f58788ddd45f90c4ebc3ce2c04fb1d4c9516f35ac2d9daef8263cd21c537bd7a52ab320f03e4ba9521677959ae20e3d405356b420 - languageName: node - linkType: hard - -"@jest/environment@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/environment@npm:30.4.1" - dependencies: - "@jest/fake-timers": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - jest-mock: "npm:30.4.1" - checksum: 10c0/704987ff8650c91a8ed13796ce47e9c55da3c12a01902d9e384330cead18eb4d34ce665a8d9962dddf2736fac006f92efc1039b8da424adf8fdc16f8d81aff6c - languageName: node - linkType: hard - -"@jest/expect-utils@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/expect-utils@npm:30.4.1" - dependencies: - "@jest/get-type": "npm:30.1.0" - checksum: 10c0/6dea9e11ebcc7be68fea5950ae5a1b7ff9fd1490101ee8af0aede336b9934ab24a28bcafe2f1171dac0f95982406386c609ca2659b9132e1a9d419e8d69b9cd4 - languageName: node - linkType: hard - -"@jest/expect@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/expect@npm:30.4.1" - dependencies: - expect: "npm:30.4.1" - jest-snapshot: "npm:30.4.1" - checksum: 10c0/2133183e735982879408036237b115abc2e57fa52bb7324be0a1f2ab6941a57da93b2e6f498dc110b7d007dd20463013fbcc5b24377cf65e6a8518d3b2ff76bd - languageName: node - linkType: hard - -"@jest/fake-timers@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/fake-timers@npm:30.4.1" - dependencies: - "@jest/types": "npm:30.4.1" - "@sinonjs/fake-timers": "npm:^15.4.0" - "@types/node": "npm:*" - jest-message-util: "npm:30.4.1" - jest-mock: "npm:30.4.1" - jest-util: "npm:30.4.1" - checksum: 10c0/4a10e4eb64bb5ea2531cdcc79f3058731f5c14faf2a74f498fcb37f6690c3c0f9b12a9856736d26e34631eb38db12e12812da71de27b9d332df44dda9f460fbe - languageName: node - linkType: hard - -"@jest/get-type@npm:30.1.0": - version: 30.1.0 - resolution: "@jest/get-type@npm:30.1.0" - checksum: 10c0/3e65fd5015f551c51ec68fca31bbd25b466be0e8ee8075d9610fa1c686ea1e70a942a0effc7b10f4ea9a338c24337e1ad97ff69d3ebacc4681b7e3e80d1b24ac - languageName: node - linkType: hard - -"@jest/globals@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/globals@npm:30.4.1" - dependencies: - "@jest/environment": "npm:30.4.1" - "@jest/expect": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - jest-mock: "npm:30.4.1" - checksum: 10c0/7961eefdc9e69ba7754d11a1bae4bc2960f33e03d9c1d6c73f27895b8cf92a9118a234330f31dc8efe16e835fe70ef9cc6c26f60121f6b6e9fac71c8b1bcd709 - languageName: node - linkType: hard - -"@jest/pattern@npm:30.4.0": - version: 30.4.0 - resolution: "@jest/pattern@npm:30.4.0" - dependencies: - "@types/node": "npm:*" - jest-regex-util: "npm:30.4.0" - checksum: 10c0/05bc0799f84f3750bbbff0f9a546979efd0dbcee86c1be98b9e2811a68885809ec7b5cca39b8dda1497cb7cf17b7be936019fba8dfbcd9c53b181e03e67f4f82 - languageName: node - linkType: hard - -"@jest/reporters@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/reporters@npm:30.4.1" - dependencies: - "@bcoe/v8-coverage": "npm:^0.2.3" - "@jest/console": "npm:30.4.1" - "@jest/test-result": "npm:30.4.1" - "@jest/transform": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@jridgewell/trace-mapping": "npm:^0.3.25" - "@types/node": "npm:*" - chalk: "npm:^4.1.2" - collect-v8-coverage: "npm:^1.0.2" - exit-x: "npm:^0.2.2" - glob: "npm:^10.5.0" - graceful-fs: "npm:^4.2.11" - istanbul-lib-coverage: "npm:^3.0.0" - istanbul-lib-instrument: "npm:^6.0.0" - istanbul-lib-report: "npm:^3.0.0" - istanbul-lib-source-maps: "npm:^5.0.0" - istanbul-reports: "npm:^3.1.3" - jest-message-util: "npm:30.4.1" - jest-util: "npm:30.4.1" - jest-worker: "npm:30.4.1" - slash: "npm:^3.0.0" - string-length: "npm:^4.0.2" - v8-to-istanbul: "npm:^9.0.1" - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - checksum: 10c0/cf5220462c6242fa564bbeb6d5988ebfd814e0351f3bddae07323b55c68c7ebd4aa4c23e717231ab4b2d63c4fc7fa4615b9dad8584be534bd44622981242dceb - languageName: node - linkType: hard - -"@jest/schemas@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/schemas@npm:30.4.1" - dependencies: - "@sinclair/typebox": "npm:^0.34.0" - checksum: 10c0/96f388ebfc1974457fcbde2ad36c40a0b549cba3f624fe8d9d6e5903a152dc75e4043f4ac9ac7668622f2ecb0f9a4dcb9a38edf3bc0d52b82045b2bb2b69b72a - languageName: node - linkType: hard - -"@jest/snapshot-utils@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/snapshot-utils@npm:30.4.1" - dependencies: - "@jest/types": "npm:30.4.1" - chalk: "npm:^4.1.2" - graceful-fs: "npm:^4.2.11" - natural-compare: "npm:^1.4.0" - checksum: 10c0/81da9079719eece02b89c45cb97162b5b7d794981652c8d8fe2846843ac81ce219ea4bc21bde7cf76c9032006435f82bd9aee8d6139d90b77078ddad4865af02 - languageName: node - linkType: hard - -"@jest/source-map@npm:30.0.1": - version: 30.0.1 - resolution: "@jest/source-map@npm:30.0.1" - dependencies: - "@jridgewell/trace-mapping": "npm:^0.3.25" - callsites: "npm:^3.1.0" - graceful-fs: "npm:^4.2.11" - checksum: 10c0/e7bda2786fc9f483d9dd7566c58c4bd948830997be862dfe80a3ae5550ff3f84753abb52e705d02ebe9db9f34ba7ebec4c2db11882048cdeef7a66f6332b3897 - languageName: node - linkType: hard - -"@jest/test-result@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/test-result@npm:30.4.1" - dependencies: - "@jest/console": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/istanbul-lib-coverage": "npm:^2.0.6" - collect-v8-coverage: "npm:^1.0.2" - checksum: 10c0/920fa3fe3cc8b5e11bfe36066d733030f1245865d7cac4862e3783a96f9c0a087fd8073c8cb56e4c87c6fcc97b46e6f828ecd3b10dd8e208f5e1b983fcc5cdb8 - languageName: node - linkType: hard - -"@jest/test-sequencer@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/test-sequencer@npm:30.4.1" - dependencies: - "@jest/test-result": "npm:30.4.1" - graceful-fs: "npm:^4.2.11" - jest-haste-map: "npm:30.4.1" - slash: "npm:^3.0.0" - checksum: 10c0/531b19ffb2358b3b22a56b306359acf66db2073978dd6df8a9522b5b4034ad7540a9cb84bdfebbcb2872686d6d2ab8cabea04ad23ef9d4488cbafd03f7511501 - languageName: node - linkType: hard - -"@jest/transform@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/transform@npm:30.4.1" - dependencies: - "@babel/core": "npm:^7.27.4" - "@jest/types": "npm:30.4.1" - "@jridgewell/trace-mapping": "npm:^0.3.25" - babel-plugin-istanbul: "npm:^7.0.1" - chalk: "npm:^4.1.2" - convert-source-map: "npm:^2.0.0" - fast-json-stable-stringify: "npm:^2.1.0" - graceful-fs: "npm:^4.2.11" - jest-haste-map: "npm:30.4.1" - jest-regex-util: "npm:30.4.0" - jest-util: "npm:30.4.1" - pirates: "npm:^4.0.7" - slash: "npm:^3.0.0" - write-file-atomic: "npm:^5.0.1" - checksum: 10c0/194f463f179f6ab3ccd6f4f0f03a117e3c01a7ce098ebf562250aca4c900ed3a9ec08b694227788eabd7cb4e0597f1d0788077c7550ddc679f68a0ad21cc87e0 - languageName: node - linkType: hard - -"@jest/types@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/types@npm:30.4.1" - dependencies: - "@jest/pattern": "npm:30.4.0" - "@jest/schemas": "npm:30.4.1" - "@types/istanbul-lib-coverage": "npm:^2.0.6" - "@types/istanbul-reports": "npm:^3.0.4" - "@types/node": "npm:*" - "@types/yargs": "npm:^17.0.33" - chalk: "npm:^4.1.2" - checksum: 10c0/4c79f6dbdb1c7eaab5da255fc696c7cae744759d4020e42da8aa63b37fe55ce594be73075fe1ee5407dd59d7e47975be9f674bfc81e91bae2c89c62d27ba55a1 - languageName: node - linkType: hard - "@jridgewell/gen-mapping@npm:^0.3.12, @jridgewell/gen-mapping@npm:^0.3.13, @jridgewell/gen-mapping@npm:^0.3.5": version: 0.3.13 resolution: "@jridgewell/gen-mapping@npm:0.3.13" @@ -1409,7 +916,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:0.3.31, @jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.28": +"@jridgewell/trace-mapping@npm:0.3.31, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.28": version: 0.3.31 resolution: "@jridgewell/trace-mapping@npm:0.3.31" dependencies: @@ -1500,20 +1007,6 @@ __metadata: languageName: node linkType: hard -"@pkgjs/parseargs@npm:^0.11.0": - version: 0.11.0 - resolution: "@pkgjs/parseargs@npm:0.11.0" - checksum: 10c0/5bd7576bb1b38a47a7fc7b51ac9f38748e772beebc56200450c4a817d712232b8f1d3ef70532c80840243c657d491cf6a6be1e3a214cff907645819fdc34aadd - languageName: node - linkType: hard - -"@pkgr/core@npm:^0.3.6": - version: 0.3.6 - resolution: "@pkgr/core@npm:0.3.6" - checksum: 10c0/153f0f4563f505faeba13c733efa0e05e467ce1c6b941055a5fd3b4560da60fbf1dff4b11da0075f034ddda11f2842b90395f60895dde5825875b616edccc11c - languageName: node - linkType: hard - "@popperjs/core@npm:^2.11.8": version: 2.11.8 resolution: "@popperjs/core@npm:2.11.8" @@ -1916,31 +1409,6 @@ __metadata: languageName: node linkType: hard -"@sinclair/typebox@npm:^0.34.0": - version: 0.34.49 - resolution: "@sinclair/typebox@npm:0.34.49" - checksum: 10c0/16b7d87f039a49b68c10bb4cdcae2ce5242b2472228851fd6483731616aba4ef977690aa517b230a8d20da8185bb416eb34e326f30568b3963c1cf26b05d1ad8 - languageName: node - linkType: hard - -"@sinonjs/commons@npm:^3.0.1": - version: 3.0.1 - resolution: "@sinonjs/commons@npm:3.0.1" - dependencies: - type-detect: "npm:4.0.8" - checksum: 10c0/1227a7b5bd6c6f9584274db996d7f8cee2c8c350534b9d0141fc662eaf1f292ea0ae3ed19e5e5271c8fd390d27e492ca2803acd31a1978be2cdc6be0da711403 - languageName: node - linkType: hard - -"@sinonjs/fake-timers@npm:^15.4.0": - version: 15.4.0 - resolution: "@sinonjs/fake-timers@npm:15.4.0" - dependencies: - "@sinonjs/commons": "npm:^3.0.1" - checksum: 10c0/de4522afe0699fa8d3ae9d1715cbaa4b47e518c707bb7988a9ec6c7c67557d9f6df451f6be0338598b984a86f65aab9fab38dd9ce75a3c0ffb801a9500d5b10d - languageName: node - linkType: hard - "@sourceacademy/common-autocomplete@npm:^0.0.1, @sourceacademy/common-autocomplete@workspace:src/common/autocomplete": version: 0.0.0-use.local resolution: "@sourceacademy/common-autocomplete@workspace:src/common/autocomplete" @@ -1977,10 +1445,7 @@ __metadata: "@rollup/plugin-node-resolve": "catalog:" "@rollup/plugin-terser": "catalog:" "@rollup/plugin-typescript": "catalog:" - "@types/jest": "npm:^30.0.0" - jest: "npm:^30.4.2" rollup: "catalog:" - ts-jest: "npm:^29.4.11" tslib: "catalog:" typescript: "catalog:" languageName: unknown @@ -1996,7 +1461,6 @@ __metadata: rollup: "catalog:" tslib: "catalog:" typescript: "catalog:" - vitest: "catalog:" languageName: unknown linkType: soft @@ -2007,12 +1471,10 @@ __metadata: "@rollup/plugin-node-resolve": "catalog:" "@rollup/plugin-terser": "catalog:" "@rollup/plugin-typescript": "catalog:" - "@types/jest": "npm:^30.0.0" - jest: "npm:^30.4.2" rollup: "catalog:" - ts-jest: "npm:^29.4.11" tslib: "catalog:" typescript: "catalog:" + vitest: "catalog:" languageName: unknown linkType: soft @@ -2137,12 +1599,10 @@ __metadata: "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-data-visualizer": "workspace:*" "@sourceacademy/conductor": "catalog:" - "@types/jest": "npm:^30.0.0" - jest: "npm:^30.4.2" rollup: "catalog:" - ts-jest: "npm:^29.4.11" tslib: "catalog:" typescript: "catalog:" + vitest: "catalog:" peerDependencies: "@sourceacademy/conductor": "*" languageName: unknown @@ -2197,12 +1657,10 @@ __metadata: "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-stepper": "workspace:*" "@sourceacademy/conductor": "catalog:" - "@types/jest": "npm:^30.0.0" - jest: "npm:^30.4.2" rollup: "catalog:" - ts-jest: "npm:^29.4.11" tslib: "catalog:" typescript: "catalog:" + vitest: "catalog:" peerDependencies: "@sourceacademy/conductor": "*" languageName: unknown @@ -2396,47 +1854,6 @@ __metadata: languageName: node linkType: hard -"@types/babel__core@npm:^7.20.5": - version: 7.20.5 - resolution: "@types/babel__core@npm:7.20.5" - dependencies: - "@babel/parser": "npm:^7.20.7" - "@babel/types": "npm:^7.20.7" - "@types/babel__generator": "npm:*" - "@types/babel__template": "npm:*" - "@types/babel__traverse": "npm:*" - checksum: 10c0/bdee3bb69951e833a4b811b8ee9356b69a61ed5b7a23e1a081ec9249769117fa83aaaf023bb06562a038eb5845155ff663e2d5c75dd95c1d5ccc91db012868ff - languageName: node - linkType: hard - -"@types/babel__generator@npm:*": - version: 7.27.0 - resolution: "@types/babel__generator@npm:7.27.0" - dependencies: - "@babel/types": "npm:^7.0.0" - checksum: 10c0/9f9e959a8792df208a9d048092fda7e1858bddc95c6314857a8211a99e20e6830bdeb572e3587ae8be5429e37f2a96fcf222a9f53ad232f5537764c9e13a2bbd - languageName: node - linkType: hard - -"@types/babel__template@npm:*": - version: 7.4.4 - resolution: "@types/babel__template@npm:7.4.4" - dependencies: - "@babel/parser": "npm:^7.1.0" - "@babel/types": "npm:^7.0.0" - checksum: 10c0/cc84f6c6ab1eab1427e90dd2b76ccee65ce940b778a9a67be2c8c39e1994e6f5bbc8efa309f6cea8dc6754994524cd4d2896558df76d92e7a1f46ecffee7112b - languageName: node - linkType: hard - -"@types/babel__traverse@npm:*": - version: 7.28.0 - resolution: "@types/babel__traverse@npm:7.28.0" - dependencies: - "@babel/types": "npm:^7.28.2" - checksum: 10c0/b52d7d4e8fc6a9018fe7361c4062c1c190f5778cf2466817cb9ed19d69fbbb54f9a85ffedeb748ed8062d2cf7d4cc088ee739848f47c57740de1c48cbf0d0994 - languageName: node - linkType: hard - "@types/chai@npm:^5.2.2": version: 5.2.3 resolution: "@types/chai@npm:5.2.3" @@ -2475,41 +1892,6 @@ __metadata: languageName: node linkType: hard -"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.1, @types/istanbul-lib-coverage@npm:^2.0.6": - version: 2.0.6 - resolution: "@types/istanbul-lib-coverage@npm:2.0.6" - checksum: 10c0/3948088654f3eeb45363f1db158354fb013b362dba2a5c2c18c559484d5eb9f6fd85b23d66c0a7c2fcfab7308d0a585b14dadaca6cc8bf89ebfdc7f8f5102fb7 - languageName: node - linkType: hard - -"@types/istanbul-lib-report@npm:*": - version: 3.0.3 - resolution: "@types/istanbul-lib-report@npm:3.0.3" - dependencies: - "@types/istanbul-lib-coverage": "npm:*" - checksum: 10c0/247e477bbc1a77248f3c6de5dadaae85ff86ac2d76c5fc6ab1776f54512a745ff2a5f791d22b942e3990ddbd40f3ef5289317c4fca5741bedfaa4f01df89051c - languageName: node - linkType: hard - -"@types/istanbul-reports@npm:^3.0.4": - version: 3.0.4 - resolution: "@types/istanbul-reports@npm:3.0.4" - dependencies: - "@types/istanbul-lib-report": "npm:*" - checksum: 10c0/1647fd402aced5b6edac87274af14ebd6b3a85447ef9ad11853a70fd92a98d35f81a5d3ea9fcb5dbb5834e800c6e35b64475e33fcae6bfa9acc70d61497c54ee - languageName: node - linkType: hard - -"@types/jest@npm:^30.0.0": - version: 30.0.0 - resolution: "@types/jest@npm:30.0.0" - dependencies: - expect: "npm:^30.0.0" - pretty-format: "npm:^30.0.0" - checksum: 10c0/20c6ce574154bc16f8dd6a97afacca4b8c4921a819496a3970382031c509ebe87a1b37b152a1b8475089b82d8ca951a9e95beb4b9bf78fbf579b1536f0b65969 - languageName: node - linkType: hard - "@types/json-schema@npm:^7.0.15": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" @@ -2517,15 +1899,6 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*": - version: 26.0.1 - resolution: "@types/node@npm:26.0.1" - dependencies: - undici-types: "npm:~8.3.0" - checksum: 10c0/31d204333c70124da6bcac7d1f27d8980149fe3f95a8419bfcd19c3e5823705c0e370d71ac34399352e1263b2d5fc41c87af964ec81e5a05a32224d65d8d224e - languageName: node - linkType: hard - "@types/node@npm:^12.7.1": version: 12.20.55 resolution: "@types/node@npm:12.20.55" @@ -2585,29 +1958,6 @@ __metadata: languageName: node linkType: hard -"@types/stack-utils@npm:^2.0.3": - version: 2.0.3 - resolution: "@types/stack-utils@npm:2.0.3" - checksum: 10c0/1f4658385ae936330581bcb8aa3a066df03867d90281cdf89cc356d404bd6579be0f11902304e1f775d92df22c6dd761d4451c804b0a4fba973e06211e9bd77c - languageName: node - linkType: hard - -"@types/yargs-parser@npm:*": - version: 21.0.3 - resolution: "@types/yargs-parser@npm:21.0.3" - checksum: 10c0/e71c3bd9d0b73ca82e10bee2064c384ab70f61034bbfb78e74f5206283fc16a6d85267b606b5c22cb2a3338373586786fed595b2009825d6a9115afba36560a0 - languageName: node - linkType: hard - -"@types/yargs@npm:^17.0.33": - version: 17.0.35 - resolution: "@types/yargs@npm:17.0.35" - dependencies: - "@types/yargs-parser": "npm:*" - checksum: 10c0/609557826a6b85e73ccf587923f6429850d6dc70e420b455bab4601b670bfadf684b09ae288bccedab042c48ba65f1666133cf375814204b544009f57d6eef63 - languageName: node - linkType: hard - "@typescript-eslint/eslint-plugin@npm:8.65.0": version: 8.65.0 resolution: "@typescript-eslint/eslint-plugin@npm:8.65.0" @@ -2743,171 +2093,6 @@ __metadata: languageName: node linkType: hard -"@ungap/structured-clone@npm:^1.3.0": - version: 1.3.2 - resolution: "@ungap/structured-clone@npm:1.3.2" - checksum: 10c0/4d76bb376ec3e15f38bdffe045377807c79057daf54ae17eeb977c5b95efddd2d726b38c15aeb5d5c1a45c64ad03aa7e8b1a6dc67895480cba536ffd1c7a06ec - languageName: node - linkType: hard - -"@unrs/resolver-binding-android-arm-eabi@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-android-arm-eabi@npm:1.12.2" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - -"@unrs/resolver-binding-android-arm64@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-android-arm64@npm:1.12.2" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - -"@unrs/resolver-binding-darwin-arm64@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-darwin-arm64@npm:1.12.2" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@unrs/resolver-binding-darwin-x64@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-darwin-x64@npm:1.12.2" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@unrs/resolver-binding-freebsd-x64@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-freebsd-x64@npm:1.12.2" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.12.2" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-arm-musleabihf@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-linux-arm-musleabihf@npm:1.12.2" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-arm64-gnu@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-linux-arm64-gnu@npm:1.12.2" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-arm64-musl@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-linux-arm64-musl@npm:1.12.2" - conditions: os=linux & cpu=arm64 & libc=musl - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-loong64-gnu@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-linux-loong64-gnu@npm:1.12.2" - conditions: os=linux & cpu=loong64 & libc=glibc - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-loong64-musl@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-linux-loong64-musl@npm:1.12.2" - conditions: os=linux & cpu=loong64 & libc=musl - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-ppc64-gnu@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-linux-ppc64-gnu@npm:1.12.2" - conditions: os=linux & cpu=ppc64 & libc=glibc - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-riscv64-gnu@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-linux-riscv64-gnu@npm:1.12.2" - conditions: os=linux & cpu=riscv64 & libc=glibc - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-riscv64-musl@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-linux-riscv64-musl@npm:1.12.2" - conditions: os=linux & cpu=riscv64 & libc=musl - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-s390x-gnu@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-linux-s390x-gnu@npm:1.12.2" - conditions: os=linux & cpu=s390x & libc=glibc - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-x64-gnu@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-linux-x64-gnu@npm:1.12.2" - conditions: os=linux & cpu=x64 & libc=glibc - languageName: node - linkType: hard - -"@unrs/resolver-binding-linux-x64-musl@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-linux-x64-musl@npm:1.12.2" - conditions: os=linux & cpu=x64 & libc=musl - languageName: node - linkType: hard - -"@unrs/resolver-binding-openharmony-arm64@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-openharmony-arm64@npm:1.12.2" - conditions: os=openharmony & cpu=arm64 - languageName: node - linkType: hard - -"@unrs/resolver-binding-wasm32-wasi@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-wasm32-wasi@npm:1.12.2" - dependencies: - "@emnapi/core": "npm:1.10.0" - "@emnapi/runtime": "npm:1.10.0" - "@napi-rs/wasm-runtime": "npm:^1.1.4" - conditions: cpu=wasm32 - languageName: node - linkType: hard - -"@unrs/resolver-binding-win32-arm64-msvc@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-win32-arm64-msvc@npm:1.12.2" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@unrs/resolver-binding-win32-ia32-msvc@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-win32-ia32-msvc@npm:1.12.2" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"@unrs/resolver-binding-win32-x64-msvc@npm:1.12.2": - version: 1.12.2 - resolution: "@unrs/resolver-binding-win32-x64-msvc@npm:1.12.2" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - "@vitest/coverage-istanbul@npm:^4.1.9": version: 4.1.10 resolution: "@vitest/coverage-istanbul@npm:4.1.10" @@ -3063,15 +2248,6 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^4.3.2": - version: 4.3.2 - resolution: "ansi-escapes@npm:4.3.2" - dependencies: - type-fest: "npm:^0.21.3" - checksum: 10c0/da917be01871525a3dfcf925ae2977bc59e8c513d4423368645634bf5d4ceba5401574eb705c1e92b79f7292af5a656f78c5725a4b0e1cec97c4b413705c1d50 - languageName: node - linkType: hard - "ansi-regex@npm:^5.0.1": version: 5.0.1 resolution: "ansi-regex@npm:5.0.1" @@ -3079,46 +2255,6 @@ __metadata: languageName: node linkType: hard -"ansi-regex@npm:^6.2.2": - version: 6.2.2 - resolution: "ansi-regex@npm:6.2.2" - checksum: 10c0/05d4acb1d2f59ab2cf4b794339c7b168890d44dda4bf0ce01152a8da0213aca207802f930442ce8cd22d7a92f44907664aac6508904e75e038fa944d2601b30f - languageName: node - linkType: hard - -"ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0": - version: 4.3.0 - resolution: "ansi-styles@npm:4.3.0" - dependencies: - color-convert: "npm:^2.0.1" - checksum: 10c0/895a23929da416f2bd3de7e9cb4eabd340949328ab85ddd6e484a637d8f6820d485f53933446f5291c3b760cbc488beb8e88573dd0f9c7daf83dccc8fe81b041 - languageName: node - linkType: hard - -"ansi-styles@npm:^5.2.0": - version: 5.2.0 - resolution: "ansi-styles@npm:5.2.0" - checksum: 10c0/9c4ca80eb3c2fb7b33841c210d2f20807f40865d27008d7c3f707b7f95cab7d67462a565e2388ac3285b71cb3d9bb2173de8da37c57692a362885ec34d6e27df - languageName: node - linkType: hard - -"ansi-styles@npm:^6.1.0": - version: 6.2.3 - resolution: "ansi-styles@npm:6.2.3" - checksum: 10c0/23b8a4ce14e18fb854693b95351e286b771d23d8844057ed2e7d083cd3e708376c3323707ec6a24365f7d7eda3ca00327fe04092e29e551499ec4c8b7bfac868 - languageName: node - linkType: hard - -"anymatch@npm:^3.1.3": - version: 3.1.3 - resolution: "anymatch@npm:3.1.3" - dependencies: - normalize-path: "npm:^3.0.0" - picomatch: "npm:^2.0.4" - checksum: 10c0/57b06ae984bc32a0d22592c87384cd88fe4511b1dd7581497831c56d41939c8a001b28e7b853e1450f2bf61992dfcaa8ae2d0d161a0a90c4fb631ef07098fbac - languageName: node - linkType: hard - "argparse@npm:^1.0.7": version: 1.0.10 resolution: "argparse@npm:1.0.10" @@ -3149,89 +2285,6 @@ __metadata: languageName: node linkType: hard -"babel-jest@npm:30.4.1": - version: 30.4.1 - resolution: "babel-jest@npm:30.4.1" - dependencies: - "@jest/transform": "npm:30.4.1" - "@types/babel__core": "npm:^7.20.5" - babel-plugin-istanbul: "npm:^7.0.1" - babel-preset-jest: "npm:30.4.0" - chalk: "npm:^4.1.2" - graceful-fs: "npm:^4.2.11" - slash: "npm:^3.0.0" - peerDependencies: - "@babel/core": ^7.11.0 || ^8.0.0-0 - checksum: 10c0/339b449011f31dc9eb18d9c49f0bb84e8de284e1107e64159a2f4a432bbd532d6a729774a56b7fbe76f5ddd716a0b4b7ad737265feab23b4d0225489b79a6f72 - languageName: node - linkType: hard - -"babel-plugin-istanbul@npm:^7.0.1": - version: 7.0.1 - resolution: "babel-plugin-istanbul@npm:7.0.1" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.0.0" - "@istanbuljs/load-nyc-config": "npm:^1.0.0" - "@istanbuljs/schema": "npm:^0.1.3" - istanbul-lib-instrument: "npm:^6.0.2" - test-exclude: "npm:^6.0.0" - checksum: 10c0/92975e3df12503b168695463b451468da0c20e117807221652eb8e33a26c160f3b9d4c5c4e65495657420e871c6a54e5e31f539e2e1da37ef2261d7ddd4b1dfd - languageName: node - linkType: hard - -"babel-plugin-jest-hoist@npm:30.4.0": - version: 30.4.0 - resolution: "babel-plugin-jest-hoist@npm:30.4.0" - dependencies: - "@types/babel__core": "npm:^7.20.5" - checksum: 10c0/1738ed536bb5ff536b4d406b8db7dbbd76cf10f80bb20d902e6efdda79898f045b9a991124d7104d8c398d0bd995d511d57694952645fba0f6250595a45277b0 - languageName: node - linkType: hard - -"babel-preset-current-node-syntax@npm:^1.2.0": - version: 1.2.0 - resolution: "babel-preset-current-node-syntax@npm:1.2.0" - dependencies: - "@babel/plugin-syntax-async-generators": "npm:^7.8.4" - "@babel/plugin-syntax-bigint": "npm:^7.8.3" - "@babel/plugin-syntax-class-properties": "npm:^7.12.13" - "@babel/plugin-syntax-class-static-block": "npm:^7.14.5" - "@babel/plugin-syntax-import-attributes": "npm:^7.24.7" - "@babel/plugin-syntax-import-meta": "npm:^7.10.4" - "@babel/plugin-syntax-json-strings": "npm:^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" - "@babel/plugin-syntax-numeric-separator": "npm:^7.10.4" - "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.3" - "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3" - "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" - "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5" - "@babel/plugin-syntax-top-level-await": "npm:^7.14.5" - peerDependencies: - "@babel/core": ^7.0.0 || ^8.0.0-0 - checksum: 10c0/94a4f81cddf9b051045d08489e4fff7336292016301664c138cfa3d9ffe3fe2ba10a24ad6ae589fd95af1ac72ba0216e1653555c187e694d7b17be0c002bea10 - languageName: node - linkType: hard - -"babel-preset-jest@npm:30.4.0": - version: 30.4.0 - resolution: "babel-preset-jest@npm:30.4.0" - dependencies: - babel-plugin-jest-hoist: "npm:30.4.0" - babel-preset-current-node-syntax: "npm:^1.2.0" - peerDependencies: - "@babel/core": ^7.11.0 || ^8.0.0-beta.1 - checksum: 10c0/ca2623aa4d8bf82b1fd01e5724a87cea7f80ff089341cf12415e9ce4b10f74838ecc6c8a48921f421f90bcd44f7929c0ad300146082e2f400253adb97ab5eb3a - languageName: node - linkType: hard - -"balanced-match@npm:^1.0.0": - version: 1.0.2 - resolution: "balanced-match@npm:1.0.2" - checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee - languageName: node - linkType: hard - "balanced-match@npm:^4.0.2": version: 4.0.4 resolution: "balanced-match@npm:4.0.4" @@ -3257,25 +2310,6 @@ __metadata: languageName: node linkType: hard -"brace-expansion@npm:^1.1.7": - version: 1.1.16 - resolution: "brace-expansion@npm:1.1.16" - dependencies: - balanced-match: "npm:^1.0.0" - concat-map: "npm:0.0.1" - checksum: 10c0/b2a915bbedbf4e45840d1fb9a4d391bbf26a79475bd134714d3cee34f1f0edb0ce982738028843be5fbaf8039429f71fa487df8c915b6065ced542c83e58fae6 - languageName: node - linkType: hard - -"brace-expansion@npm:^2.0.2": - version: 2.1.1 - resolution: "brace-expansion@npm:2.1.1" - dependencies: - balanced-match: "npm:^1.0.0" - checksum: 10c0/63b5ddce608b70b50a76817c0526faf8ea67a9180073d88bb402f6bbc22a22da6b1dfac4f65efc53e5faa80222fb7d44bbf2fc638c3f55365975573f671d0ccb - languageName: node - linkType: hard - "brace-expansion@npm:^5.0.5": version: 5.0.5 resolution: "brace-expansion@npm:5.0.5" @@ -3309,24 +2343,6 @@ __metadata: languageName: node linkType: hard -"bs-logger@npm:^0.2.6": - version: 0.2.6 - resolution: "bs-logger@npm:0.2.6" - dependencies: - fast-json-stable-stringify: "npm:2.x" - checksum: 10c0/80e89aaaed4b68e3374ce936f2eb097456a0dddbf11f75238dbd53140b1e39259f0d248a5089ed456f1158984f22191c3658d54a713982f676709fbe1a6fa5a0 - languageName: node - linkType: hard - -"bser@npm:2.1.1": - version: 2.1.1 - resolution: "bser@npm:2.1.1" - dependencies: - node-int64: "npm:^0.4.0" - checksum: 10c0/24d8dfb7b6d457d73f32744e678a60cc553e4ec0e9e1a01cf614b44d85c3c87e188d3cc78ef0442ce5032ee6818de20a0162ba1074725c0d08908f62ea979227 - languageName: node - linkType: hard - "buffer-from@npm:^1.0.0": version: 1.1.2 resolution: "buffer-from@npm:1.1.2" @@ -3334,13 +2350,6 @@ __metadata: languageName: node linkType: hard -"callsites@npm:^3.1.0": - version: 3.1.0 - resolution: "callsites@npm:3.1.0" - checksum: 10c0/fff92277400eb06c3079f9e74f3af120db9f8ea03bad0e84d9aede54bbe2d44a56cccb5f6cf12211f93f52306df87077ecec5b712794c5a9b5dac6d615a3f301 - languageName: node - linkType: hard - "camel-case@npm:^4.1.2": version: 4.1.2 resolution: "camel-case@npm:4.1.2" @@ -3351,20 +2360,6 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^5.3.1": - version: 5.3.1 - resolution: "camelcase@npm:5.3.1" - checksum: 10c0/92ff9b443bfe8abb15f2b1513ca182d16126359ad4f955ebc83dc4ddcc4ef3fdd2c078bc223f2673dc223488e75c99b16cc4d056624374b799e6a1555cf61b23 - languageName: node - linkType: hard - -"camelcase@npm:^6.3.0": - version: 6.3.0 - resolution: "camelcase@npm:6.3.0" - checksum: 10c0/0d701658219bd3116d12da3eab31acddb3f9440790c0792e0d398f0a520a6a4058018e546862b6fba89d7ae990efaeb97da71e1913e9ebf5a8b5621a3d55c710 - languageName: node - linkType: hard - "caniuse-lite@npm:^1.0.30001782": version: 1.0.30001791 resolution: "caniuse-lite@npm:1.0.30001791" @@ -3390,16 +2385,6 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.1.2": - version: 4.1.2 - resolution: "chalk@npm:4.1.2" - dependencies: - ansi-styles: "npm:^4.1.0" - supports-color: "npm:^7.1.0" - checksum: 10c0/4a3fef5cc34975c898ffe77141450f679721df9dde00f6c304353fa9c8b571929123b26a0e4617bde5018977eb655b31970c297b91b63ee83bb82aeb04666880 - languageName: node - linkType: hard - "change-case@npm:^4.1.2": version: 4.1.2 resolution: "change-case@npm:4.1.2" @@ -3420,13 +2405,6 @@ __metadata: languageName: node linkType: hard -"char-regex@npm:^1.0.2": - version: 1.0.2 - resolution: "char-regex@npm:1.0.2" - checksum: 10c0/57a09a86371331e0be35d9083ba429e86c4f4648ecbe27455dbfb343037c16ee6fdc7f6b61f433a57cc5ded5561d71c56a150e018f40c2ffb7bc93a26dae341e - languageName: node - linkType: hard - "chardet@npm:^2.1.1": version: 2.1.1 resolution: "chardet@npm:2.1.1" @@ -3441,20 +2419,6 @@ __metadata: languageName: node linkType: hard -"ci-info@npm:^4.2.0": - version: 4.4.0 - resolution: "ci-info@npm:4.4.0" - checksum: 10c0/44156201545b8dde01aa8a09ee2fe9fc7a73b1bef9adbd4606c9f61c8caeeb73fb7a575c88b0443f7b4edb5ee45debaa59ed54ba5f99698339393ca01349eb3a - languageName: node - linkType: hard - -"cjs-module-lexer@npm:^2.1.0": - version: 2.2.0 - resolution: "cjs-module-lexer@npm:2.2.0" - checksum: 10c0/aec4ca58f87145fac221386790ecaae8b012f2e2359a45acb61d8c75ea4fa84f6ea869f17abc1a7e91a808eff0fed581209632f03540de16f72f0a28f5fd35ac - languageName: node - linkType: hard - "classnames@npm:^2.3.1, classnames@npm:^2.3.2": version: 2.5.1 resolution: "classnames@npm:2.5.1" @@ -3462,47 +2426,6 @@ __metadata: languageName: node linkType: hard -"cliui@npm:^8.0.1": - version: 8.0.1 - resolution: "cliui@npm:8.0.1" - dependencies: - string-width: "npm:^4.2.0" - strip-ansi: "npm:^6.0.1" - wrap-ansi: "npm:^7.0.0" - checksum: 10c0/4bda0f09c340cbb6dfdc1ed508b3ca080f12992c18d68c6be4d9cf51756033d5266e61ec57529e610dacbf4da1c634423b0c1b11037709cc6b09045cbd815df5 - languageName: node - linkType: hard - -"co@npm:^4.6.0": - version: 4.6.0 - resolution: "co@npm:4.6.0" - checksum: 10c0/c0e85ea0ca8bf0a50cbdca82efc5af0301240ca88ebe3644a6ffb8ffe911f34d40f8fbcf8f1d52c5ddd66706abd4d3bfcd64259f1e8e2371d4f47573b0dc8c28 - languageName: node - linkType: hard - -"collect-v8-coverage@npm:^1.0.2": - version: 1.0.3 - resolution: "collect-v8-coverage@npm:1.0.3" - checksum: 10c0/bc62ba251bcce5e3354a8f88fa6442bee56e3e612fec08d4dfcf66179b41ea0bf544b0f78c4ebc0f8050871220af95bb5c5578a6aef346feea155640582f09dc - languageName: node - linkType: hard - -"color-convert@npm:^2.0.1": - version: 2.0.1 - resolution: "color-convert@npm:2.0.1" - dependencies: - color-name: "npm:~1.1.4" - checksum: 10c0/37e1150172f2e311fe1b2df62c6293a342ee7380da7b9cfdba67ea539909afbd74da27033208d01d6d5cfc65ee7868a22e18d7e7648e004425441c0f8a15a7d7 - languageName: node - linkType: hard - -"color-name@npm:~1.1.4": - version: 1.1.4 - resolution: "color-name@npm:1.1.4" - checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95 - languageName: node - linkType: hard - "commander@npm:^15.0.0": version: 15.0.0 resolution: "commander@npm:15.0.0" @@ -3531,13 +2454,6 @@ __metadata: languageName: node linkType: hard -"concat-map@npm:0.0.1": - version: 0.0.1 - resolution: "concat-map@npm:0.0.1" - checksum: 10c0/c996b1cfdf95b6c90fee4dae37e332c8b6eb7d106430c17d538034c0ad9a1630cb194d2ab37293b1bdd4d779494beee7786d586a50bd9376fd6f7bcc2bd4c98f - languageName: node - linkType: hard - "constant-case@npm:^3.0.4": version: 3.0.4 resolution: "constant-case@npm:3.0.4" @@ -3556,7 +2472,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.5, cross-spawn@npm:^7.0.6": +"cross-spawn@npm:^7.0.5, cross-spawn@npm:^7.0.6": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" dependencies: @@ -3574,7 +2490,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.4.3": +"debug@npm:^4.1.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.4.3": version: 4.4.3 resolution: "debug@npm:4.4.3" dependencies: @@ -3593,18 +2509,6 @@ __metadata: languageName: node linkType: hard -"dedent@npm:^1.6.0": - version: 1.7.2 - resolution: "dedent@npm:1.7.2" - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true - checksum: 10c0/acaff07cac355b93f17b1b17ebbb84d3cc55af6ab4b7814c3f505e061903e168bc6bf9ddce331552d64dee1525f0b4c549c9ade46aebfac6f69caaed74e90751 - languageName: node - linkType: hard - "deep-is@npm:^0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" @@ -3612,7 +2516,7 @@ __metadata: languageName: node linkType: hard -"deepmerge@npm:^4.2.2, deepmerge@npm:^4.3.1": +"deepmerge@npm:^4.2.2": version: 4.3.1 resolution: "deepmerge@npm:4.3.1" checksum: 10c0/e53481aaf1aa2c4082b5342be6b6d8ad9dfe387bc92ce197a66dea08bd4265904a087e75e464f14d1347cf2ac8afe1e4c16b266e0561cc5df29382d3c5f80044 @@ -3633,13 +2537,6 @@ __metadata: languageName: node linkType: hard -"detect-newline@npm:^3.1.0": - version: 3.1.0 - resolution: "detect-newline@npm:3.1.0" - checksum: 10c0/c38cfc8eeb9fda09febb44bcd85e467c970d4e3bf526095394e5a4f18bc26dd0cf6b22c69c1fa9969261521c593836db335c2795218f6d781a512aea2fb8209d - languageName: node - linkType: hard - "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -3676,13 +2573,6 @@ __metadata: languageName: node linkType: hard -"eastasianwidth@npm:^0.2.0": - version: 0.2.0 - resolution: "eastasianwidth@npm:0.2.0" - checksum: 10c0/26f364ebcdb6395f95124fda411f63137a4bfb5d3a06453f7f23dfe52502905bd84e0488172e0f9ec295fdc45f05c23d5d91baf16bd26f0fe9acd777a188dc39 - languageName: node - linkType: hard - "electron-to-chromium@npm:^1.5.328": version: 1.5.349 resolution: "electron-to-chromium@npm:1.5.349" @@ -3690,27 +2580,6 @@ __metadata: languageName: node linkType: hard -"emittery@npm:^0.13.1": - version: 0.13.1 - resolution: "emittery@npm:0.13.1" - checksum: 10c0/1573d0ae29ab34661b6c63251ff8f5facd24ccf6a823f19417ae8ba8c88ea450325788c67f16c99edec8de4b52ce93a10fe441ece389fd156e88ee7dab9bfa35 - languageName: node - linkType: hard - -"emoji-regex@npm:^8.0.0": - version: 8.0.0 - resolution: "emoji-regex@npm:8.0.0" - checksum: 10c0/b6053ad39951c4cf338f9092d7bfba448cdfd46fe6a2a034700b149ac9ffbc137e361cbd3c442297f86bed2e5f7576c1b54cc0a6bf8ef5106cc62f496af35010 - languageName: node - linkType: hard - -"emoji-regex@npm:^9.2.2": - version: 9.2.2 - resolution: "emoji-regex@npm:9.2.2" - checksum: 10c0/af014e759a72064cf66e6e694a7fc6b0ed3d8db680427b021a89727689671cefe9d04151b2cad51dbaf85d5ba790d061cd167f1cf32eb7b281f6368b3c181639 - languageName: node - linkType: hard - "enquirer@npm:^2.4.1": version: 2.4.1 resolution: "enquirer@npm:2.4.1" @@ -3728,15 +2597,6 @@ __metadata: languageName: node linkType: hard -"error-ex@npm:^1.3.1": - version: 1.3.4 - resolution: "error-ex@npm:1.3.4" - dependencies: - is-arrayish: "npm:^0.2.1" - checksum: 10c0/b9e34ff4778b8f3b31a8377e1c654456f4c41aeaa3d10a1138c3b7635d8b7b2e03eb2475d46d8ae055c1f180a1063e100bffabf64ea7e7388b37735df5328664 - languageName: node - linkType: hard - "es-errors@npm:^1.3.0": version: 1.3.0 resolution: "es-errors@npm:1.3.0" @@ -3840,7 +2700,7 @@ __metadata: languageName: node linkType: hard -"escalade@npm:^3.1.1, escalade@npm:^3.2.0": +"escalade@npm:^3.2.0": version: 3.2.0 resolution: "escalade@npm:3.2.0" checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65 @@ -3854,13 +2714,6 @@ __metadata: languageName: node linkType: hard -"escape-string-regexp@npm:^2.0.0": - version: 2.0.0 - resolution: "escape-string-regexp@npm:2.0.0" - checksum: 10c0/2530479fe8db57eace5e8646c9c2a9c80fa279614986d16dcc6bcaceb63ae77f05a851ba6c43756d816c61d7f4534baf56e3c705e3e0d884818a46808811c507 - languageName: node - linkType: hard - "escape-string-regexp@npm:^4.0.0": version: 4.0.0 resolution: "escape-string-regexp@npm:4.0.0" @@ -4019,30 +2872,6 @@ __metadata: languageName: node linkType: hard -"execa@npm:^5.1.1": - version: 5.1.1 - resolution: "execa@npm:5.1.1" - dependencies: - cross-spawn: "npm:^7.0.3" - get-stream: "npm:^6.0.0" - human-signals: "npm:^2.1.0" - is-stream: "npm:^2.0.0" - merge-stream: "npm:^2.0.0" - npm-run-path: "npm:^4.0.1" - onetime: "npm:^5.1.2" - signal-exit: "npm:^3.0.3" - strip-final-newline: "npm:^2.0.0" - checksum: 10c0/c8e615235e8de4c5addf2fa4c3da3e3aa59ce975a3e83533b4f6a71750fb816a2e79610dc5f1799b6e28976c9ae86747a36a606655bf8cb414a74d8d507b304f - languageName: node - linkType: hard - -"exit-x@npm:^0.2.2": - version: 0.2.2 - resolution: "exit-x@npm:0.2.2" - checksum: 10c0/212a7a095ca5540e9581f1ef2d1d6a40df7a6027c8cc96e78ce1d16b86d1a88326d4a0eff8dff2b5ec1e68bb0c1edd5d0dfdde87df1869bf7514d4bc6a5cbd72 - languageName: node - linkType: hard - "expect-type@npm:^1.3.0": version: 1.3.0 resolution: "expect-type@npm:1.3.0" @@ -4050,20 +2879,6 @@ __metadata: languageName: node linkType: hard -"expect@npm:30.4.1, expect@npm:^30.0.0": - version: 30.4.1 - resolution: "expect@npm:30.4.1" - dependencies: - "@jest/expect-utils": "npm:30.4.1" - "@jest/get-type": "npm:30.1.0" - jest-matcher-utils: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-mock: "npm:30.4.1" - jest-util: "npm:30.4.1" - checksum: 10c0/ad04fbdffac5a2bae186478938a60f737e3aac823db9a80c87f3f390f9f458bddcc454dc3a3997d715706747c6aff928923e6a71db3a221adb89a51cc1582e72 - languageName: node - linkType: hard - "exponential-backoff@npm:^3.1.1": version: 3.1.3 resolution: "exponential-backoff@npm:3.1.3" @@ -4098,7 +2913,7 @@ __metadata: languageName: node linkType: hard -"fast-json-stable-stringify@npm:2.x, fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0": +"fast-json-stable-stringify@npm:^2.0.0": version: 2.1.0 resolution: "fast-json-stable-stringify@npm:2.1.0" checksum: 10c0/7f081eb0b8a64e0057b3bb03f974b3ef00135fbf36c1c710895cd9300f13c94ba809bb3a81cf4e1b03f6e5285610a61abbd7602d0652de423144dfee5a389c9b @@ -4137,15 +2952,6 @@ __metadata: languageName: node linkType: hard -"fb-watchman@npm:^2.0.2": - version: 2.0.2 - resolution: "fb-watchman@npm:2.0.2" - dependencies: - bser: "npm:2.1.1" - checksum: 10c0/feae89ac148adb8f6ae8ccd87632e62b13563e6fb114cacb5265c51f585b17e2e268084519fb2edd133872f1d47a18e6bfd7e5e08625c0d41b93149694187581 - languageName: node - linkType: hard - "fdir@npm:^6.2.0, fdir@npm:^6.5.0": version: 6.5.0 resolution: "fdir@npm:6.5.0" @@ -4176,7 +2982,7 @@ __metadata: languageName: node linkType: hard -"find-up@npm:^4.0.0, find-up@npm:^4.1.0": +"find-up@npm:^4.1.0": version: 4.1.0 resolution: "find-up@npm:4.1.0" dependencies: @@ -4213,16 +3019,6 @@ __metadata: languageName: node linkType: hard -"foreground-child@npm:^3.1.0": - version: 3.3.1 - resolution: "foreground-child@npm:3.3.1" - dependencies: - cross-spawn: "npm:^7.0.6" - signal-exit: "npm:^4.0.1" - checksum: 10c0/8986e4af2430896e65bc2788d6679067294d6aee9545daefc84923a0a4b399ad9c7a3ea7bd8c0b2b80fdf4a92de4c69df3f628233ff3224260e9c1541a9e9ed3 - languageName: node - linkType: hard - "fraction.js@npm:^5.2.1": version: 5.3.4 resolution: "fraction.js@npm:5.3.4" @@ -4252,14 +3048,7 @@ __metadata: languageName: node linkType: hard -"fs.realpath@npm:^1.0.0": - version: 1.0.0 - resolution: "fs.realpath@npm:1.0.0" - checksum: 10c0/444cf1291d997165dfd4c0d58b69f0e4782bfd9149fd72faa4fe299e68e0e93d6db941660b37dd29153bf7186672ececa3b50b7e7249477b03fdf850f287c948 - languageName: node - linkType: hard - -"fsevents@npm:^2.3.3, fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": +"fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": version: 2.3.3 resolution: "fsevents@npm:2.3.3" dependencies: @@ -4269,7 +3058,7 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@npm%3A^2.3.3#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": +"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": version: 2.3.3 resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" dependencies: @@ -4292,27 +3081,6 @@ __metadata: languageName: node linkType: hard -"get-caller-file@npm:^2.0.5": - version: 2.0.5 - resolution: "get-caller-file@npm:2.0.5" - checksum: 10c0/c6c7b60271931fa752aeb92f2b47e355eac1af3a2673f47c9589e8f8a41adc74d45551c1bc57b5e66a80609f10ffb72b6f575e4370d61cc3f7f3aaff01757cde - languageName: node - linkType: hard - -"get-package-type@npm:^0.1.0": - version: 0.1.0 - resolution: "get-package-type@npm:0.1.0" - checksum: 10c0/e34cdf447fdf1902a1f6d5af737eaadf606d2ee3518287abde8910e04159368c268568174b2e71102b87b26c2020486f126bfca9c4fb1ceb986ff99b52ecd1be - languageName: node - linkType: hard - -"get-stream@npm:^6.0.0": - version: 6.0.1 - resolution: "get-stream@npm:6.0.1" - checksum: 10c0/49825d57d3fd6964228e6200a58169464b8e8970489b3acdc24906c782fb7f01f9f56f8e6653c4a50713771d6658f7cfe051e5eb8c12e334138c9c918b296341 - languageName: node - linkType: hard - "glob-parent@npm:^5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" @@ -4331,36 +3099,6 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.5.0": - version: 10.5.0 - resolution: "glob@npm:10.5.0" - dependencies: - foreground-child: "npm:^3.1.0" - jackspeak: "npm:^3.1.2" - minimatch: "npm:^9.0.4" - minipass: "npm:^7.1.2" - package-json-from-dist: "npm:^1.0.0" - path-scurry: "npm:^1.11.1" - bin: - glob: dist/esm/bin.mjs - checksum: 10c0/100705eddbde6323e7b35e1d1ac28bcb58322095bd8e63a7d0bef1a2cdafe0d0f7922a981b2b48369a4f8c1b077be5c171804534c3509dfe950dde15fbe6d828 - languageName: node - linkType: hard - -"glob@npm:^7.1.4": - version: 7.2.3 - resolution: "glob@npm:7.2.3" - dependencies: - fs.realpath: "npm:^1.0.0" - inflight: "npm:^1.0.4" - inherits: "npm:2" - minimatch: "npm:^3.1.1" - once: "npm:^1.3.0" - path-is-absolute: "npm:^1.0.0" - checksum: 10c0/65676153e2b0c9095100fe7f25a778bf45608eeb32c6048cf307f579649bcc30353277b3b898a3792602c65764e5baa4f643714dfbdfd64ea271d210c7a425fe - languageName: node - linkType: hard - "globby@npm:^11.0.0": version: 11.1.0 resolution: "globby@npm:11.1.0" @@ -4375,31 +3113,13 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.6": +"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 languageName: node linkType: hard -"handlebars@npm:^4.7.9": - version: 4.7.9 - resolution: "handlebars@npm:4.7.9" - dependencies: - minimist: "npm:^1.2.5" - neo-async: "npm:^2.6.2" - source-map: "npm:^0.6.1" - uglify-js: "npm:^3.1.4" - wordwrap: "npm:^1.0.0" - dependenciesMeta: - uglify-js: - optional: true - bin: - handlebars: bin/handlebars - checksum: 10c0/22f8105a7e68e81aff2662bb434edf05f757d21d850731d71cec886d69c10cd33d3c43e34b2892968ec62de8241611851d3d0674c8ef324ea3e01dc66262faa9 - languageName: node - linkType: hard - "has-flag@npm:^4.0.0": version: 4.0.0 resolution: "has-flag@npm:4.0.0" @@ -4442,13 +3162,6 @@ __metadata: languageName: node linkType: hard -"human-signals@npm:^2.1.0": - version: 2.1.0 - resolution: "human-signals@npm:2.1.0" - checksum: 10c0/695edb3edfcfe9c8b52a76926cd31b36978782062c0ed9b1192b36bebc75c4c87c82e178dfcb0ed0fc27ca59d434198aac0bd0be18f5781ded775604db22304a - languageName: node - linkType: hard - "iconv-lite@npm:^0.7.0": version: 0.7.2 resolution: "iconv-lite@npm:0.7.2" @@ -4472,18 +3185,6 @@ __metadata: languageName: node linkType: hard -"import-local@npm:^3.2.0": - version: 3.2.0 - resolution: "import-local@npm:3.2.0" - dependencies: - pkg-dir: "npm:^4.2.0" - resolve-cwd: "npm:^3.0.0" - bin: - import-local-fixture: fixtures/cli.js - checksum: 10c0/94cd6367a672b7e0cb026970c85b76902d2710a64896fa6de93bd5c571dd03b228c5759308959de205083e3b1c61e799f019c9e36ee8e9c523b993e1057f0433 - languageName: node - linkType: hard - "imurmurhash@npm:^0.1.4": version: 0.1.4 resolution: "imurmurhash@npm:0.1.4" @@ -4491,30 +3192,6 @@ __metadata: languageName: node linkType: hard -"inflight@npm:^1.0.4": - version: 1.0.6 - resolution: "inflight@npm:1.0.6" - dependencies: - once: "npm:^1.3.0" - wrappy: "npm:1" - checksum: 10c0/7faca22584600a9dc5b9fca2cd5feb7135ac8c935449837b315676b4c90aa4f391ec4f42240178244b5a34e8bede1948627fda392ca3191522fc46b34e985ab2 - languageName: node - linkType: hard - -"inherits@npm:2": - version: 2.0.4 - resolution: "inherits@npm:2.0.4" - checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 - languageName: node - linkType: hard - -"is-arrayish@npm:^0.2.1": - version: 0.2.1 - resolution: "is-arrayish@npm:0.2.1" - checksum: 10c0/e7fb686a739068bb70f860b39b67afc62acc62e36bb61c5f965768abce1873b379c563e61dd2adad96ebb7edf6651111b385e490cf508378959b0ed4cac4e729 - languageName: node - linkType: hard - "is-core-module@npm:^2.16.1": version: 2.16.1 resolution: "is-core-module@npm:2.16.1" @@ -4531,20 +3208,6 @@ __metadata: languageName: node linkType: hard -"is-fullwidth-code-point@npm:^3.0.0": - version: 3.0.0 - resolution: "is-fullwidth-code-point@npm:3.0.0" - checksum: 10c0/bb11d825e049f38e04c06373a8d72782eee0205bda9d908cc550ccb3c59b99d750ff9537982e01733c1c94a58e35400661f57042158ff5e8f3e90cf936daf0fc - languageName: node - linkType: hard - -"is-generator-fn@npm:^2.1.0": - version: 2.1.0 - resolution: "is-generator-fn@npm:2.1.0" - checksum: 10c0/2957cab387997a466cd0bf5c1b6047bd21ecb32bdcfd8996b15747aa01002c1c88731802f1b3d34ac99f4f6874b626418bd118658cf39380fe5fff32a3af9c4d - languageName: node - linkType: hard - "is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3": version: 4.0.3 resolution: "is-glob@npm:4.0.3" @@ -4577,13 +3240,6 @@ __metadata: languageName: node linkType: hard -"is-stream@npm:^2.0.0": - version: 2.0.1 - resolution: "is-stream@npm:2.0.1" - checksum: 10c0/7c284241313fc6efc329b8d7f08e16c0efeb6baab1b4cd0ba579eb78e5af1aa5da11e68559896a2067cd6c526bd29241dda4eb1225e627d5aa1a89a76d4635a5 - languageName: node - linkType: hard - "is-subdir@npm:^1.1.1": version: 1.2.0 resolution: "is-subdir@npm:1.2.0" @@ -4614,26 +3270,13 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0, istanbul-lib-coverage@npm:^3.2.2": +"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.2": version: 3.2.2 resolution: "istanbul-lib-coverage@npm:3.2.2" checksum: 10c0/6c7ff2106769e5f592ded1fb418f9f73b4411fd5a084387a5410538332b6567cd1763ff6b6cadca9b9eb2c443cce2f7ea7d7f1b8d315f9ce58539793b1e0922b languageName: node linkType: hard -"istanbul-lib-instrument@npm:^6.0.0, istanbul-lib-instrument@npm:^6.0.2": - version: 6.0.3 - resolution: "istanbul-lib-instrument@npm:6.0.3" - dependencies: - "@babel/core": "npm:^7.23.9" - "@babel/parser": "npm:^7.23.9" - "@istanbuljs/schema": "npm:^0.1.3" - istanbul-lib-coverage: "npm:^3.2.0" - semver: "npm:^7.5.4" - checksum: 10c0/a1894e060dd2a3b9f046ffdc87b44c00a35516f5e6b7baf4910369acca79e506fc5323a816f811ae23d82334b38e3ddeb8b3b331bd2c860540793b59a8689128 - languageName: node - linkType: hard - "istanbul-lib-report@npm:^3.0.0, istanbul-lib-report@npm:^3.0.1": version: 3.0.1 resolution: "istanbul-lib-report@npm:3.0.1" @@ -4645,18 +3288,7 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-source-maps@npm:^5.0.0": - version: 5.0.6 - resolution: "istanbul-lib-source-maps@npm:5.0.6" - dependencies: - "@jridgewell/trace-mapping": "npm:^0.3.23" - debug: "npm:^4.1.1" - istanbul-lib-coverage: "npm:^3.0.0" - checksum: 10c0/ffe75d70b303a3621ee4671554f306e0831b16f39ab7f4ab52e54d356a5d33e534d97563e318f1333a6aae1d42f91ec49c76b6cd3f3fb378addcb5c81da0255f - languageName: node - linkType: hard - -"istanbul-reports@npm:^3.1.3, istanbul-reports@npm:^3.2.0": +"istanbul-reports@npm:^3.2.0": version: 3.2.0 resolution: "istanbul-reports@npm:3.2.0" dependencies: @@ -4677,19 +3309,6 @@ __metadata: languageName: node linkType: hard -"jackspeak@npm:^3.1.2": - version: 3.4.3 - resolution: "jackspeak@npm:3.4.3" - dependencies: - "@isaacs/cliui": "npm:^8.0.2" - "@pkgjs/parseargs": "npm:^0.11.0" - dependenciesMeta: - "@pkgjs/parseargs": - optional: true - checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9 - languageName: node - linkType: hard - "javascript-natural-sort@npm:^0.7.1": version: 0.7.1 resolution: "javascript-natural-sort@npm:0.7.1" @@ -4697,444 +3316,6 @@ __metadata: languageName: node linkType: hard -"jest-changed-files@npm:30.4.1": - version: 30.4.1 - resolution: "jest-changed-files@npm:30.4.1" - dependencies: - execa: "npm:^5.1.1" - jest-util: "npm:30.4.1" - p-limit: "npm:^3.1.0" - checksum: 10c0/324bbec3920a7d9ceb1d11872b9f1befe73d152a7ef289243f663bf3b22afe124c2c656ec316e44393f30a83b74a1738b56307a066906fa49b800686fd4d0f04 - languageName: node - linkType: hard - -"jest-circus@npm:30.4.2": - version: 30.4.2 - resolution: "jest-circus@npm:30.4.2" - dependencies: - "@jest/environment": "npm:30.4.1" - "@jest/expect": "npm:30.4.1" - "@jest/test-result": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - chalk: "npm:^4.1.2" - co: "npm:^4.6.0" - dedent: "npm:^1.6.0" - is-generator-fn: "npm:^2.1.0" - jest-each: "npm:30.4.1" - jest-matcher-utils: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-runtime: "npm:30.4.2" - jest-snapshot: "npm:30.4.1" - jest-util: "npm:30.4.1" - p-limit: "npm:^3.1.0" - pretty-format: "npm:30.4.1" - pure-rand: "npm:^7.0.0" - slash: "npm:^3.0.0" - stack-utils: "npm:^2.0.6" - checksum: 10c0/5d99f1336eb249057063a007fabad4ced802501fbaad7ddeea8db9553fa54fbd44d26e71e8bf61a0979d42b3b93a3d920e6f00afa26cdbb70d1e7d0969515d10 - languageName: node - linkType: hard - -"jest-cli@npm:30.4.2": - version: 30.4.2 - resolution: "jest-cli@npm:30.4.2" - dependencies: - "@jest/core": "npm:30.4.2" - "@jest/test-result": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - chalk: "npm:^4.1.2" - exit-x: "npm:^0.2.2" - import-local: "npm:^3.2.0" - jest-config: "npm:30.4.2" - jest-util: "npm:30.4.1" - jest-validate: "npm:30.4.1" - yargs: "npm:^17.7.2" - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - bin: - jest: ./bin/jest.js - checksum: 10c0/a036a1bf06ce7d5fed644a518c4a4ccf60c5fe5f3d96d143973048e6690c4a28a4f97fa3275d90ca236430a1b2a7c10544e7e190a4f2edfdf0a4e6daf1f6a384 - languageName: node - linkType: hard - -"jest-config@npm:30.4.2": - version: 30.4.2 - resolution: "jest-config@npm:30.4.2" - dependencies: - "@babel/core": "npm:^7.27.4" - "@jest/get-type": "npm:30.1.0" - "@jest/pattern": "npm:30.4.0" - "@jest/test-sequencer": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - babel-jest: "npm:30.4.1" - chalk: "npm:^4.1.2" - ci-info: "npm:^4.2.0" - deepmerge: "npm:^4.3.1" - glob: "npm:^10.5.0" - graceful-fs: "npm:^4.2.11" - jest-circus: "npm:30.4.2" - jest-docblock: "npm:30.4.0" - jest-environment-node: "npm:30.4.1" - jest-regex-util: "npm:30.4.0" - jest-resolve: "npm:30.4.1" - jest-runner: "npm:30.4.2" - jest-util: "npm:30.4.1" - jest-validate: "npm:30.4.1" - parse-json: "npm:^5.2.0" - pretty-format: "npm:30.4.1" - slash: "npm:^3.0.0" - strip-json-comments: "npm:^3.1.1" - peerDependencies: - "@types/node": "*" - esbuild-register: ">=3.4.0" - ts-node: ">=9.0.0" - peerDependenciesMeta: - "@types/node": - optional: true - esbuild-register: - optional: true - ts-node: - optional: true - checksum: 10c0/18300b1dc54a4bfb5d1db6c10aeb01b6c64736224e3f60d119da9504d49cbab5a76d789f38c44af7d168418463356db6843ad7e44f249c63ce7f409758eba0c6 - languageName: node - linkType: hard - -"jest-diff@npm:30.4.1": - version: 30.4.1 - resolution: "jest-diff@npm:30.4.1" - dependencies: - "@jest/diff-sequences": "npm:30.4.0" - "@jest/get-type": "npm:30.1.0" - chalk: "npm:^4.1.2" - pretty-format: "npm:30.4.1" - checksum: 10c0/787e11f0ea27e94815479d6c5415e4173da1e74bede34c1515b8515fc9d1fe053e2ad25a3c31f9998a7292c186a0e4d395ed82e0e149d57d7708ee6759b442e9 - languageName: node - linkType: hard - -"jest-docblock@npm:30.4.0": - version: 30.4.0 - resolution: "jest-docblock@npm:30.4.0" - dependencies: - detect-newline: "npm:^3.1.0" - checksum: 10c0/1fe1c971207e1b905e4f23d98e508a03ae631337e9ffa347ff2f6df81a1d75ced7ed3e52a809fad75fb8a8cd55b6bda4483bc124e5e1d7529eeb4ef76b29e913 - languageName: node - linkType: hard - -"jest-each@npm:30.4.1": - version: 30.4.1 - resolution: "jest-each@npm:30.4.1" - dependencies: - "@jest/get-type": "npm:30.1.0" - "@jest/types": "npm:30.4.1" - chalk: "npm:^4.1.2" - jest-util: "npm:30.4.1" - pretty-format: "npm:30.4.1" - checksum: 10c0/41bc1cec23901cb0c7d8f547a70574fffca8cc16a1660ed97645bf3b61f4e6151aaa58bb14ce55a3cd9f5a63a2cc782a39366caf3304a2159d1e3cc5ae79a9e4 - languageName: node - linkType: hard - -"jest-environment-node@npm:30.4.1": - version: 30.4.1 - resolution: "jest-environment-node@npm:30.4.1" - dependencies: - "@jest/environment": "npm:30.4.1" - "@jest/fake-timers": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - jest-mock: "npm:30.4.1" - jest-util: "npm:30.4.1" - jest-validate: "npm:30.4.1" - checksum: 10c0/d8d6bb22bfd280f077b5856558d9d7112c48fd3bae6eda9b76694f1c8e1be783a725686a137437d180c9d49e6b37386c8e342e0b8e5bfcb6526dee9c10cc31ec - languageName: node - linkType: hard - -"jest-haste-map@npm:30.4.1": - version: 30.4.1 - resolution: "jest-haste-map@npm:30.4.1" - dependencies: - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - anymatch: "npm:^3.1.3" - fb-watchman: "npm:^2.0.2" - fsevents: "npm:^2.3.3" - graceful-fs: "npm:^4.2.11" - jest-regex-util: "npm:30.4.0" - jest-util: "npm:30.4.1" - jest-worker: "npm:30.4.1" - picomatch: "npm:^4.0.3" - walker: "npm:^1.0.8" - dependenciesMeta: - fsevents: - optional: true - checksum: 10c0/1350c24952bbf31c86cb1ed4e2e5edd4766a93e2be8816c4648c05463d06cfae89f3c73732f9274fdb626fdfdfe6605ed6f259b6c21257df536a6379d4b9a5e7 - languageName: node - linkType: hard - -"jest-leak-detector@npm:30.4.1": - version: 30.4.1 - resolution: "jest-leak-detector@npm:30.4.1" - dependencies: - "@jest/get-type": "npm:30.1.0" - pretty-format: "npm:30.4.1" - checksum: 10c0/57256ac08f12186e3ed1687126b8d75a12de9c4ffa959ff41322e9ba5f93e3ed8af91dc36bc4d59f77cef6d4008bcf5a3e646cdd950743898576aec8dbae6778 - languageName: node - linkType: hard - -"jest-matcher-utils@npm:30.4.1": - version: 30.4.1 - resolution: "jest-matcher-utils@npm:30.4.1" - dependencies: - "@jest/get-type": "npm:30.1.0" - chalk: "npm:^4.1.2" - jest-diff: "npm:30.4.1" - pretty-format: "npm:30.4.1" - checksum: 10c0/ddbb0c7075def27ba30160883c327cb3fd13f561f5789d00a1edca1b48b0651f8ea23a1c51bcfcb6413a68c47d658bcf47a34701b8a39ce135dd28d87a3117af - languageName: node - linkType: hard - -"jest-message-util@npm:30.4.1": - version: 30.4.1 - resolution: "jest-message-util@npm:30.4.1" - dependencies: - "@babel/code-frame": "npm:^7.27.1" - "@jest/types": "npm:30.4.1" - "@types/stack-utils": "npm:^2.0.3" - chalk: "npm:^4.1.2" - graceful-fs: "npm:^4.2.11" - jest-util: "npm:30.4.1" - picomatch: "npm:^4.0.3" - pretty-format: "npm:30.4.1" - slash: "npm:^3.0.0" - stack-utils: "npm:^2.0.6" - checksum: 10c0/ae7427544e042bc1c14abf3c0dbe8b83d0dbec22a9a5efefaca5b8ccb6b9bf391abe732e6f2117ca995c6889bfe1be35c78cec75e5ea0a50e28cffe1ba6f9fdf - languageName: node - linkType: hard - -"jest-mock@npm:30.4.1": - version: 30.4.1 - resolution: "jest-mock@npm:30.4.1" - dependencies: - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - jest-util: "npm:30.4.1" - checksum: 10c0/5185a41255285c1634c5d85dda037afaaadfc12793b3293c9e253a30bb67449f8df968447f830abb9cf7a52e63694e6734680130e8085ce119056280890bf6fc - languageName: node - linkType: hard - -"jest-pnp-resolver@npm:^1.2.3": - version: 1.2.3 - resolution: "jest-pnp-resolver@npm:1.2.3" - peerDependencies: - jest-resolve: "*" - peerDependenciesMeta: - jest-resolve: - optional: true - checksum: 10c0/86eec0c78449a2de733a6d3e316d49461af6a858070e113c97f75fb742a48c2396ea94150cbca44159ffd4a959f743a47a8b37a792ef6fdad2cf0a5cba973fac - languageName: node - linkType: hard - -"jest-regex-util@npm:30.4.0": - version: 30.4.0 - resolution: "jest-regex-util@npm:30.4.0" - checksum: 10c0/fe7426f67b54d38bed8e9d6e6a099d63d72f41f5bf65b922d9d03fedcb55c614b45657207632f6ee22d0a59d8d11327891f258d23f68a58912fcdb0f7db48435 - languageName: node - linkType: hard - -"jest-resolve-dependencies@npm:30.4.2": - version: 30.4.2 - resolution: "jest-resolve-dependencies@npm:30.4.2" - dependencies: - jest-regex-util: "npm:30.4.0" - jest-snapshot: "npm:30.4.1" - checksum: 10c0/4101afabd2a4ef4e6c82bf82ea145286c1238373f7611938e8d47ddcf5aaa6e10af365436a934b7af194451e351774829cb021ac73f857b4873dcccc7aabb616 - languageName: node - linkType: hard - -"jest-resolve@npm:30.4.1": - version: 30.4.1 - resolution: "jest-resolve@npm:30.4.1" - dependencies: - chalk: "npm:^4.1.2" - graceful-fs: "npm:^4.2.11" - jest-haste-map: "npm:30.4.1" - jest-pnp-resolver: "npm:^1.2.3" - jest-util: "npm:30.4.1" - jest-validate: "npm:30.4.1" - slash: "npm:^3.0.0" - unrs-resolver: "npm:^1.7.11" - checksum: 10c0/0a99ef4f4fd7b3678d58a5e1cf8f0b5ec1997cdba21f5d66a8b26353d57a226f8e6a5fffc450c8836e90ab0e20d5e7935d0dea939d9a9b6a08781b9a7413184c - languageName: node - linkType: hard - -"jest-runner@npm:30.4.2": - version: 30.4.2 - resolution: "jest-runner@npm:30.4.2" - dependencies: - "@jest/console": "npm:30.4.1" - "@jest/environment": "npm:30.4.1" - "@jest/test-result": "npm:30.4.1" - "@jest/transform": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - chalk: "npm:^4.1.2" - emittery: "npm:^0.13.1" - exit-x: "npm:^0.2.2" - graceful-fs: "npm:^4.2.11" - jest-docblock: "npm:30.4.0" - jest-environment-node: "npm:30.4.1" - jest-haste-map: "npm:30.4.1" - jest-leak-detector: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-resolve: "npm:30.4.1" - jest-runtime: "npm:30.4.2" - jest-util: "npm:30.4.1" - jest-watcher: "npm:30.4.1" - jest-worker: "npm:30.4.1" - p-limit: "npm:^3.1.0" - source-map-support: "npm:0.5.13" - checksum: 10c0/339e630fb1a7db52e208ed9f12f722122733fe9a450d9bd83c0fccc10fbc5142a8808f624c41ab1e25833af02f9c3eca85561554b75a5b3ad75b4a226f72c5cf - languageName: node - linkType: hard - -"jest-runtime@npm:30.4.2": - version: 30.4.2 - resolution: "jest-runtime@npm:30.4.2" - dependencies: - "@jest/environment": "npm:30.4.1" - "@jest/fake-timers": "npm:30.4.1" - "@jest/globals": "npm:30.4.1" - "@jest/source-map": "npm:30.0.1" - "@jest/test-result": "npm:30.4.1" - "@jest/transform": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - chalk: "npm:^4.1.2" - cjs-module-lexer: "npm:^2.1.0" - collect-v8-coverage: "npm:^1.0.2" - glob: "npm:^10.5.0" - graceful-fs: "npm:^4.2.11" - jest-haste-map: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-mock: "npm:30.4.1" - jest-regex-util: "npm:30.4.0" - jest-resolve: "npm:30.4.1" - jest-snapshot: "npm:30.4.1" - jest-util: "npm:30.4.1" - slash: "npm:^3.0.0" - strip-bom: "npm:^4.0.0" - checksum: 10c0/9fce55b0c78fbe47dc2c10a944e9513833fd43c14f292460ef5cdd91e375088bf35549336e66f69fc9d29bf4f410894e9a7eef0bf12a6f39d99174a5300c2c53 - languageName: node - linkType: hard - -"jest-snapshot@npm:30.4.1": - version: 30.4.1 - resolution: "jest-snapshot@npm:30.4.1" - dependencies: - "@babel/core": "npm:^7.27.4" - "@babel/generator": "npm:^7.27.5" - "@babel/plugin-syntax-jsx": "npm:^7.27.1" - "@babel/plugin-syntax-typescript": "npm:^7.27.1" - "@babel/types": "npm:^7.27.3" - "@jest/expect-utils": "npm:30.4.1" - "@jest/get-type": "npm:30.1.0" - "@jest/snapshot-utils": "npm:30.4.1" - "@jest/transform": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - babel-preset-current-node-syntax: "npm:^1.2.0" - chalk: "npm:^4.1.2" - expect: "npm:30.4.1" - graceful-fs: "npm:^4.2.11" - jest-diff: "npm:30.4.1" - jest-matcher-utils: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-util: "npm:30.4.1" - pretty-format: "npm:30.4.1" - semver: "npm:^7.7.2" - synckit: "npm:^0.11.8" - checksum: 10c0/cebd70277b6f0d2606f22815480146cf1e37295ed69a1d16e260a99a2ab48db167857e2fb9a938923d22ac13203c83a5e31d7f066b58d87c6d42db58c914ff13 - languageName: node - linkType: hard - -"jest-util@npm:30.4.1": - version: 30.4.1 - resolution: "jest-util@npm:30.4.1" - dependencies: - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - chalk: "npm:^4.1.2" - ci-info: "npm:^4.2.0" - graceful-fs: "npm:^4.2.11" - picomatch: "npm:^4.0.3" - checksum: 10c0/3efe1f25e5a172d04c6af8612d82867ab603b7c1bd8cb89073ff834679b44eba178793cf3af162cf5e25be13aa736ebd23a7826683acc85bddc5873f305b1f6e - languageName: node - linkType: hard - -"jest-validate@npm:30.4.1": - version: 30.4.1 - resolution: "jest-validate@npm:30.4.1" - dependencies: - "@jest/get-type": "npm:30.1.0" - "@jest/types": "npm:30.4.1" - camelcase: "npm:^6.3.0" - chalk: "npm:^4.1.2" - leven: "npm:^3.1.0" - pretty-format: "npm:30.4.1" - checksum: 10c0/23e6677ee6d06476f368c8b6d442b4207e5fbe062e74c1da3eae9ed30a18605f4e8a14809fa9cc7f22a2d8446e8de91a512f59c278720db2ad61c77dc25ffefc - languageName: node - linkType: hard - -"jest-watcher@npm:30.4.1": - version: 30.4.1 - resolution: "jest-watcher@npm:30.4.1" - dependencies: - "@jest/test-result": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - ansi-escapes: "npm:^4.3.2" - chalk: "npm:^4.1.2" - emittery: "npm:^0.13.1" - jest-util: "npm:30.4.1" - string-length: "npm:^4.0.2" - checksum: 10c0/a56e1714b7b0f9c620c5cee95a84a48b780093594cd188e365a24768f208714895a0deb784ee48e4eec7f1828bc00435ab3c39208d490c33be3786937e997c97 - languageName: node - linkType: hard - -"jest-worker@npm:30.4.1": - version: 30.4.1 - resolution: "jest-worker@npm:30.4.1" - dependencies: - "@types/node": "npm:*" - "@ungap/structured-clone": "npm:^1.3.0" - jest-util: "npm:30.4.1" - merge-stream: "npm:^2.0.0" - supports-color: "npm:^8.1.1" - checksum: 10c0/3eb7ec7e928b82491e66ae6709e3a1eef3edad2bc351514a5d52037b997151989de6ce2912d6a5a3806ae3ae3bf6a1c36b1ad7bbc567d0790503fdb74576f140 - languageName: node - linkType: hard - -"jest@npm:^30.4.2": - version: 30.4.2 - resolution: "jest@npm:30.4.2" - dependencies: - "@jest/core": "npm:30.4.2" - "@jest/types": "npm:30.4.1" - import-local: "npm:^3.2.0" - jest-cli: "npm:30.4.2" - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - bin: - jest: ./bin/jest.js - checksum: 10c0/26a76eaabfc043abd8ee702b97f61ff968dde03412efdb4a69c22c99a5e4bf47788a3e45f75134aec1377a686a9d59d1e3bae85a816e409013475a80de1458ec - languageName: node - linkType: hard - "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -5142,7 +3323,7 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:^3.13.1, js-yaml@npm:^3.6.1": +"js-yaml@npm:^3.6.1": version: 3.15.0 resolution: "js-yaml@npm:3.15.0" dependencies: @@ -5181,13 +3362,6 @@ __metadata: languageName: node linkType: hard -"json-parse-even-better-errors@npm:^2.3.0": - version: 2.3.1 - resolution: "json-parse-even-better-errors@npm:2.3.1" - checksum: 10c0/140932564c8f0b88455432e0f33c4cb4086b8868e37524e07e723f4eaedb9425bdc2bafd71bd1d9765bd15fd1e2d126972bc83990f55c467168c228c24d665f3 - languageName: node - linkType: hard - "json-schema-traverse@npm:^0.4.1": version: 0.4.1 resolution: "json-schema-traverse@npm:0.4.1" @@ -5239,13 +3413,6 @@ __metadata: languageName: node linkType: hard -"leven@npm:^3.1.0": - version: 3.1.0 - resolution: "leven@npm:3.1.0" - checksum: 10c0/cd778ba3fbab0f4d0500b7e87d1f6e1f041507c56fdcd47e8256a3012c98aaee371d4c15e0a76e0386107af2d42e2b7466160a2d80688aaa03e66e49949f42df - languageName: node - linkType: hard - "levn@npm:^0.4.1": version: 0.4.1 resolution: "levn@npm:0.4.1" @@ -5376,13 +3543,6 @@ __metadata: languageName: node linkType: hard -"lines-and-columns@npm:^1.1.6": - version: 1.2.4 - resolution: "lines-and-columns@npm:1.2.4" - checksum: 10c0/3da6ee62d4cd9f03f5dc90b4df2540fb85b352081bee77fe4bbcd12c9000ead7f35e0a38b8d09a9bb99b13223446dd8689ff3c4959807620726d788701a83d2d - languageName: node - linkType: hard - "locate-path@npm:^5.0.0": version: 5.0.0 resolution: "locate-path@npm:5.0.0" @@ -5401,13 +3561,6 @@ __metadata: languageName: node linkType: hard -"lodash.memoize@npm:^4.1.2": - version: 4.1.2 - resolution: "lodash.memoize@npm:4.1.2" - checksum: 10c0/c8713e51eccc650422716a14cece1809cfe34bc5ab5e242b7f8b4e2241c2483697b971a604252807689b9dd69bfe3a98852e19a5b89d506b000b4187a1285df8 - languageName: node - linkType: hard - "lodash.startcase@npm:^4.4.0": version: 4.4.0 resolution: "lodash.startcase@npm:4.4.0" @@ -5435,13 +3588,6 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^10.2.0": - version: 10.4.3 - resolution: "lru-cache@npm:10.4.3" - checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb - languageName: node - linkType: hard - "lru-cache@npm:^5.1.1": version: 5.1.1 resolution: "lru-cache@npm:5.1.1" @@ -5480,22 +3626,6 @@ __metadata: languageName: node linkType: hard -"make-error@npm:^1.3.6": - version: 1.3.6 - resolution: "make-error@npm:1.3.6" - checksum: 10c0/171e458d86854c6b3fc46610cfacf0b45149ba043782558c6875d9f42f222124384ad0b468c92e996d815a8a2003817a710c0a160e49c1c394626f76fa45396f - languageName: node - linkType: hard - -"makeerror@npm:1.0.12": - version: 1.0.12 - resolution: "makeerror@npm:1.0.12" - dependencies: - tmpl: "npm:1.0.5" - checksum: 10c0/b0e6e599780ce6bab49cc413eba822f7d1f0dfebd1c103eaa3785c59e43e22c59018323cf9e1708f0ef5329e94a745d163fcbb6bff8e4c6742f9be9e86f3500c - languageName: node - linkType: hard - "mathjs@npm:^14.9.1": version: 14.9.1 resolution: "mathjs@npm:14.9.1" @@ -5515,13 +3645,6 @@ __metadata: languageName: node linkType: hard -"merge-stream@npm:^2.0.0": - version: 2.0.0 - resolution: "merge-stream@npm:2.0.0" - checksum: 10c0/867fdbb30a6d58b011449b8885601ec1690c3e41c759ecd5a9d609094f7aed0096c37823ff4a7190ef0b8f22cc86beb7049196ff68c016e3b3c671d0dac91ce5 - languageName: node - linkType: hard - "merge2@npm:^1.3.0, merge2@npm:^1.4.1": version: 1.4.1 resolution: "merge2@npm:1.4.1" @@ -5531,56 +3654,24 @@ __metadata: "micromatch@npm:^4.0.8": version: 4.0.8 - resolution: "micromatch@npm:4.0.8" - dependencies: - braces: "npm:^3.0.3" - picomatch: "npm:^2.3.1" - checksum: 10c0/166fa6eb926b9553f32ef81f5f531d27b4ce7da60e5baf8c021d043b27a388fb95e46a8038d5045877881e673f8134122b59624d5cecbd16eb50a42e7a6b5ca8 - languageName: node - linkType: hard - -"mimic-fn@npm:^2.1.0": - version: 2.1.0 - resolution: "mimic-fn@npm:2.1.0" - checksum: 10c0/b26f5479d7ec6cc2bce275a08f146cf78f5e7b661b18114e2506dd91ec7ec47e7a25bf4360e5438094db0560bcc868079fb3b1fb3892b833c1ecbf63f80c95a4 - languageName: node - linkType: hard - -"minimatch@npm:^10.2.2, minimatch@npm:^10.2.4, minimatch@npm:^10.2.5": - version: 10.2.5 - resolution: "minimatch@npm:10.2.5" - dependencies: - brace-expansion: "npm:^5.0.5" - checksum: 10c0/6bb058bd6324104b9ec2f763476a35386d05079c1f5fe4fbf1f324a25237cd4534d6813ecd71f48208f4e635c1221899bef94c3c89f7df55698fe373aaae20fd - languageName: node - linkType: hard - -"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1": - version: 3.1.5 - resolution: "minimatch@npm:3.1.5" - dependencies: - brace-expansion: "npm:^1.1.7" - checksum: 10c0/2ecbdc0d33f07bddb0315a8b5afbcb761307a8778b48f0b312418ccbced99f104a2d17d8aca7573433c70e8ccd1c56823a441897a45e384ea76ef401a26ace70 - languageName: node - linkType: hard - -"minimatch@npm:^9.0.4": - version: 9.0.9 - resolution: "minimatch@npm:9.0.9" + resolution: "micromatch@npm:4.0.8" dependencies: - brace-expansion: "npm:^2.0.2" - checksum: 10c0/0b6a58530dbb00361745aa6c8cffaba4c90f551afe7c734830bd95fd88ebf469dd7355a027824ea1d09e37181cfeb0a797fb17df60c15ac174303ac110eb7e86 + braces: "npm:^3.0.3" + picomatch: "npm:^2.3.1" + checksum: 10c0/166fa6eb926b9553f32ef81f5f531d27b4ce7da60e5baf8c021d043b27a388fb95e46a8038d5045877881e673f8134122b59624d5cecbd16eb50a42e7a6b5ca8 languageName: node linkType: hard -"minimist@npm:^1.2.5": - version: 1.2.8 - resolution: "minimist@npm:1.2.8" - checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6 +"minimatch@npm:^10.2.2, minimatch@npm:^10.2.4, minimatch@npm:^10.2.5": + version: 10.2.5 + resolution: "minimatch@npm:10.2.5" + dependencies: + brace-expansion: "npm:^5.0.5" + checksum: 10c0/6bb058bd6324104b9ec2f763476a35386d05079c1f5fe4fbf1f324a25237cd4534d6813ecd71f48208f4e635c1221899bef94c3c89f7df55698fe373aaae20fd languageName: node linkType: hard -"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.4, minipass@npm:^7.1.2": +"minipass@npm:^7.0.4, minipass@npm:^7.1.2": version: 7.1.3 resolution: "minipass@npm:7.1.3" checksum: 10c0/539da88daca16533211ea5a9ee98dc62ff5742f531f54640dd34429e621955e91cc280a91a776026264b7f9f6735947629f920944e9c1558369e8bf22eb33fbb @@ -5626,15 +3717,6 @@ __metadata: languageName: node linkType: hard -"napi-postinstall@npm:^0.3.4": - version: 0.3.4 - resolution: "napi-postinstall@npm:0.3.4" - bin: - napi-postinstall: lib/cli.js - checksum: 10c0/b33d64150828bdade3a5d07368a8b30da22ee393f8dd8432f1b9e5486867be21c84ec443dd875dd3ef3c7401a079a7ab7e2aa9d3538a889abbcd96495d5104fe - languageName: node - linkType: hard - "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -5659,13 +3741,6 @@ __metadata: languageName: node linkType: hard -"neo-async@npm:^2.6.2": - version: 2.6.2 - resolution: "neo-async@npm:2.6.2" - checksum: 10c0/c2f5a604a54a8ec5438a342e1f356dff4bc33ccccdb6dc668d94fe8e5eccfc9d2c2eea6064b0967a767ba63b33763f51ccf2cd2441b461a7322656c1f06b3f5d - languageName: node - linkType: hard - "no-case@npm:^3.0.4": version: 3.0.4 resolution: "no-case@npm:3.0.4" @@ -5696,13 +3771,6 @@ __metadata: languageName: node linkType: hard -"node-int64@npm:^0.4.0": - version: 0.4.0 - resolution: "node-int64@npm:0.4.0" - checksum: 10c0/a6a4d8369e2f2720e9c645255ffde909c0fbd41c92ea92a5607fc17055955daac99c1ff589d421eee12a0d24e99f7bfc2aabfeb1a4c14742f6c099a51863f31a - languageName: node - linkType: hard - "node-releases@npm:^2.0.36": version: 2.0.38 resolution: "node-releases@npm:2.0.38" @@ -5721,13 +3789,6 @@ __metadata: languageName: node linkType: hard -"normalize-path@npm:^3.0.0": - version: 3.0.0 - resolution: "normalize-path@npm:3.0.0" - checksum: 10c0/e008c8142bcc335b5e38cf0d63cfd39d6cf2d97480af9abdbe9a439221fd4d749763bab492a8ee708ce7a194bb00c9da6d0a115018672310850489137b3da046 - languageName: node - linkType: hard - "normalize.css@npm:^8.0.1": version: 8.0.1 resolution: "normalize.css@npm:8.0.1" @@ -5735,15 +3796,6 @@ __metadata: languageName: node linkType: hard -"npm-run-path@npm:^4.0.1": - version: 4.0.1 - resolution: "npm-run-path@npm:4.0.1" - dependencies: - path-key: "npm:^3.0.0" - checksum: 10c0/6f9353a95288f8455cf64cbeb707b28826a7f29690244c1e4bb61ec573256e021b6ad6651b394eb1ccfd00d6ec50147253aba2c5fe58a57ceb111fad62c519ac - languageName: node - linkType: hard - "object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" @@ -5758,24 +3810,6 @@ __metadata: languageName: node linkType: hard -"once@npm:^1.3.0": - version: 1.4.0 - resolution: "once@npm:1.4.0" - dependencies: - wrappy: "npm:1" - checksum: 10c0/5d48aca287dfefabd756621c5dfce5c91a549a93e9fdb7b8246bc4c4790aa2ec17b34a260530474635147aeb631a2dcc8b32c613df0675f96041cbb8244517d0 - languageName: node - linkType: hard - -"onetime@npm:^5.1.2": - version: 5.1.2 - resolution: "onetime@npm:5.1.2" - dependencies: - mimic-fn: "npm:^2.1.0" - checksum: 10c0/ffcef6fbb2692c3c40749f31ea2e22677a876daea92959b8a80b521d95cca7a668c884d8b2045d1d8ee7d56796aa405c405462af112a1477594cc63531baeb8f - languageName: node - linkType: hard - "optionator@npm:^0.9.3": version: 0.9.4 resolution: "optionator@npm:0.9.4" @@ -5815,7 +3849,7 @@ __metadata: languageName: node linkType: hard -"p-limit@npm:^3.0.2, p-limit@npm:^3.1.0": +"p-limit@npm:^3.0.2": version: 3.1.0 resolution: "p-limit@npm:3.1.0" dependencies: @@ -5856,13 +3890,6 @@ __metadata: languageName: node linkType: hard -"package-json-from-dist@npm:^1.0.0": - version: 1.0.1 - resolution: "package-json-from-dist@npm:1.0.1" - checksum: 10c0/62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b - languageName: node - linkType: hard - "package-manager-detector@npm:^0.2.0": version: 0.2.11 resolution: "package-manager-detector@npm:0.2.11" @@ -5882,18 +3909,6 @@ __metadata: languageName: node linkType: hard -"parse-json@npm:^5.2.0": - version: 5.2.0 - resolution: "parse-json@npm:5.2.0" - dependencies: - "@babel/code-frame": "npm:^7.0.0" - error-ex: "npm:^1.3.1" - json-parse-even-better-errors: "npm:^2.3.0" - lines-and-columns: "npm:^1.1.6" - checksum: 10c0/77947f2253005be7a12d858aedbafa09c9ae39eb4863adf330f7b416ca4f4a08132e453e08de2db46459256fb66afaac5ee758b44fe6541b7cdaf9d252e59585 - languageName: node - linkType: hard - "pascal-case@npm:^3.1.2": version: 3.1.2 resolution: "pascal-case@npm:3.1.2" @@ -5921,14 +3936,7 @@ __metadata: languageName: node linkType: hard -"path-is-absolute@npm:^1.0.0": - version: 1.0.1 - resolution: "path-is-absolute@npm:1.0.1" - checksum: 10c0/127da03c82172a2a50099cddbf02510c1791fc2cc5f7713ddb613a56838db1e8168b121a920079d052e0936c23005562059756d653b7c544c53185efe53be078 - languageName: node - linkType: hard - -"path-key@npm:^3.0.0, path-key@npm:^3.1.0": +"path-key@npm:^3.1.0": version: 3.1.1 resolution: "path-key@npm:3.1.1" checksum: 10c0/748c43efd5a569c039d7a00a03b58eecd1d75f3999f5a28303d75f521288df4823bc057d8784eb72358b2895a05f29a070bc9f1f17d28226cc4e62494cc58c4c @@ -5942,16 +3950,6 @@ __metadata: languageName: node linkType: hard -"path-scurry@npm:^1.11.1": - version: 1.11.1 - resolution: "path-scurry@npm:1.11.1" - dependencies: - lru-cache: "npm:^10.2.0" - minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" - checksum: 10c0/32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d - languageName: node - linkType: hard - "path-type@npm:^4.0.0": version: 4.0.0 resolution: "path-type@npm:4.0.0" @@ -5973,7 +3971,7 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^2.0.4, picomatch@npm:^2.3.1": +"picomatch@npm:^2.3.1": version: 2.3.2 resolution: "picomatch@npm:2.3.2" checksum: 10c0/a554d1709e59be97d1acb9eaedbbc700a5c03dbd4579807baed95100b00420bc729335440ef15004ae2378984e2487a7c1cebd743cfdb72b6fa9ab69223c0d61 @@ -5994,22 +3992,6 @@ __metadata: languageName: node linkType: hard -"pirates@npm:^4.0.7": - version: 4.0.7 - resolution: "pirates@npm:4.0.7" - checksum: 10c0/a51f108dd811beb779d58a76864bbd49e239fa40c7984cd11596c75a121a8cc789f1c8971d8bb15f0dbf9d48b76c05bb62fcbce840f89b688c0fa64b37e8478a - languageName: node - linkType: hard - -"pkg-dir@npm:^4.2.0": - version: 4.2.0 - resolution: "pkg-dir@npm:4.2.0" - dependencies: - find-up: "npm:^4.0.0" - checksum: 10c0/c56bda7769e04907a88423feb320babaed0711af8c436ce3e56763ab1021ba107c7b0cafb11cde7529f669cfc22bffcaebffb573645cbd63842ea9fb17cd7728 - languageName: node - linkType: hard - "postcss@npm:^8.5.15": version: 8.5.23 resolution: "postcss@npm:8.5.23" @@ -6046,18 +4028,6 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:30.4.1, pretty-format@npm:^30.0.0": - version: 30.4.1 - resolution: "pretty-format@npm:30.4.1" - dependencies: - "@jest/schemas": "npm:30.4.1" - ansi-styles: "npm:^5.2.0" - react-is-18: "npm:react-is@^18.3.1" - react-is-19: "npm:react-is@^19.2.5" - checksum: 10c0/c7e6633740cd2f6d382f188c00c8b4b3f2bee3cda16db6753471c6bb4b94f76531358d3a7793062a0fb00d72ebfb934e8ae1d4f5ced6bb34c8e7f60996f90076 - languageName: node - linkType: hard - "proc-log@npm:^6.0.0": version: 6.1.0 resolution: "proc-log@npm:6.1.0" @@ -6083,13 +4053,6 @@ __metadata: languageName: node linkType: hard -"pure-rand@npm:^7.0.0": - version: 7.0.1 - resolution: "pure-rand@npm:7.0.1" - checksum: 10c0/9cade41030f5ec95f5d55a11a71404cd6f46b69becaad892097cd7f58e2c6248cd0a933349ca7d21336ab629f1da42ffe899699b671bc4651600eaf6e57f837e - languageName: node - linkType: hard - "py-slang@github:proto-aiken-13/py-slang#branch-ev3-engine": version: 1.0.0 resolution: "py-slang@https://github.com/proto-aiken-13/py-slang.git#commit=da6c3870cd7c4319d250484c8edf15ef4de21555" @@ -6154,20 +4117,6 @@ __metadata: languageName: node linkType: hard -"react-is-18@npm:react-is@^18.3.1": - version: 18.3.1 - resolution: "react-is@npm:18.3.1" - checksum: 10c0/f2f1e60010c683479e74c63f96b09fb41603527cd131a9959e2aee1e5a8b0caf270b365e5ca77d4a6b18aae659b60a86150bb3979073528877029b35aecd2072 - languageName: node - linkType: hard - -"react-is-19@npm:react-is@^19.2.5": - version: 19.2.7 - resolution: "react-is@npm:19.2.7" - checksum: 10c0/419fe54d5bd7fdf5414a5bb7bd9a1e0e36f9fae28ffb4cb73290fbe342bde15d8584a90d1db62547f6aa03018dce517b178a041abb522136cd4b4b51b4e94c83 - languageName: node - linkType: hard - "react-is@npm:^16.13.1": version: 16.13.1 resolution: "react-is@npm:16.13.1" @@ -6269,22 +4218,6 @@ __metadata: languageName: node linkType: hard -"require-directory@npm:^2.1.1": - version: 2.1.1 - resolution: "require-directory@npm:2.1.1" - checksum: 10c0/83aa76a7bc1531f68d92c75a2ca2f54f1b01463cb566cf3fbc787d0de8be30c9dbc211d1d46be3497dac5785fe296f2dd11d531945ac29730643357978966e99 - languageName: node - linkType: hard - -"resolve-cwd@npm:^3.0.0": - version: 3.0.0 - resolution: "resolve-cwd@npm:3.0.0" - dependencies: - resolve-from: "npm:^5.0.0" - checksum: 10c0/e608a3ebd15356264653c32d7ecbc8fd702f94c6703ea4ac2fb81d9c359180cba0ae2e6b71faa446631ed6145454d5a56b227efc33a2d40638ac13f8beb20ee4 - languageName: node - linkType: hard - "resolve-from@npm:^5.0.0": version: 5.0.0 resolution: "resolve-from@npm:5.0.0" @@ -6521,7 +4454,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.7.2, semver@npm:^7.7.3, semver@npm:^7.8.0": +"semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.7.3": version: 7.8.5 resolution: "semver@npm:7.8.5" bin: @@ -6571,13 +4504,6 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.3": - version: 3.0.7 - resolution: "signal-exit@npm:3.0.7" - checksum: 10c0/25d272fa73e146048565e08f3309d5b942c1979a6f4a58a8c59d5fa299728e9c2fcd1a759ec870863b1fd38653670240cd420dad2ad9330c71f36608a6a1c912 - languageName: node - linkType: hard - "signal-exit@npm:^4.0.1": version: 4.1.0 resolution: "signal-exit@npm:4.1.0" @@ -6616,16 +4542,6 @@ __metadata: languageName: node linkType: hard -"source-map-support@npm:0.5.13": - version: 0.5.13 - resolution: "source-map-support@npm:0.5.13" - dependencies: - buffer-from: "npm:^1.0.0" - source-map: "npm:^0.6.0" - checksum: 10c0/137539f8c453fa0f496ea42049ab5da4569f96781f6ac8e5bfda26937be9494f4e8891f523c5f98f0e85f71b35d74127a00c46f83f6a4f54672b58d53202565e - languageName: node - linkType: hard - "source-map-support@npm:~0.5.20": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" @@ -6636,7 +4552,7 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.6.0, source-map@npm:^0.6.1": +"source-map@npm:^0.6.0": version: 0.6.1 resolution: "source-map@npm:0.6.1" checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 @@ -6660,15 +4576,6 @@ __metadata: languageName: node linkType: hard -"stack-utils@npm:^2.0.6": - version: 2.0.6 - resolution: "stack-utils@npm:2.0.6" - dependencies: - escape-string-regexp: "npm:^2.0.0" - checksum: 10c0/651c9f87667e077584bbe848acaecc6049bc71979f1e9a46c7b920cad4431c388df0f51b8ad7cfd6eed3db97a2878d0fc8b3122979439ea8bac29c61c95eec8a - languageName: node - linkType: hard - "stackback@npm:0.0.2": version: 0.0.2 resolution: "stackback@npm:0.0.2" @@ -6683,39 +4590,7 @@ __metadata: languageName: node linkType: hard -"string-length@npm:^4.0.2": - version: 4.0.2 - resolution: "string-length@npm:4.0.2" - dependencies: - char-regex: "npm:^1.0.2" - strip-ansi: "npm:^6.0.0" - checksum: 10c0/1cd77409c3d7db7bc59406f6bcc9ef0783671dcbabb23597a1177c166906ef2ee7c8290f78cae73a8aec858768f189d2cb417797df5e15ec4eb5e16b3346340c - languageName: node - linkType: hard - -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": - version: 4.2.3 - resolution: "string-width@npm:4.2.3" - dependencies: - emoji-regex: "npm:^8.0.0" - is-fullwidth-code-point: "npm:^3.0.0" - strip-ansi: "npm:^6.0.1" - checksum: 10c0/1e525e92e5eae0afd7454086eed9c818ee84374bb80328fc41217ae72ff5f065ef1c9d7f72da41de40c75fa8bb3dee63d92373fd492c84260a552c636392a47b - languageName: node - linkType: hard - -"string-width@npm:^5.0.1, string-width@npm:^5.1.2": - version: 5.1.2 - resolution: "string-width@npm:5.1.2" - dependencies: - eastasianwidth: "npm:^0.2.0" - emoji-regex: "npm:^9.2.2" - strip-ansi: "npm:^7.0.1" - checksum: 10c0/ab9c4264443d35b8b923cbdd513a089a60de339216d3b0ed3be3ba57d6880e1a192b70ae17225f764d7adbf5994e9bb8df253a944736c15a0240eff553c678ca - languageName: node - linkType: hard - -"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": +"strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" dependencies: @@ -6724,15 +4599,6 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^7.0.1": - version: 7.2.0 - resolution: "strip-ansi@npm:7.2.0" - dependencies: - ansi-regex: "npm:^6.2.2" - checksum: 10c0/544d13b7582f8254811ea97db202f519e189e59d35740c46095897e254e4f1aa9fe1524a83ad6bc5ad67d4dd6c0281d2e0219ed62b880a6238a16a17d375f221 - languageName: node - linkType: hard - "strip-bom@npm:^3.0.0": version: 3.0.0 resolution: "strip-bom@npm:3.0.0" @@ -6740,27 +4606,6 @@ __metadata: languageName: node linkType: hard -"strip-bom@npm:^4.0.0": - version: 4.0.0 - resolution: "strip-bom@npm:4.0.0" - checksum: 10c0/26abad1172d6bc48985ab9a5f96c21e440f6e7e476686de49be813b5a59b3566dccb5c525b831ec54fe348283b47f3ffb8e080bc3f965fde12e84df23f6bb7ef - languageName: node - linkType: hard - -"strip-final-newline@npm:^2.0.0": - version: 2.0.0 - resolution: "strip-final-newline@npm:2.0.0" - checksum: 10c0/bddf8ccd47acd85c0e09ad7375409d81653f645fda13227a9d459642277c253d877b68f2e5e4d819fe75733b0e626bac7e954c04f3236f6d196f79c94fa4a96f - languageName: node - linkType: hard - -"strip-json-comments@npm:^3.1.1": - version: 3.1.1 - resolution: "strip-json-comments@npm:3.1.1" - checksum: 10c0/9681a6257b925a7fa0f285851c0e613cc934a50661fa7bb41ca9cbbff89686bb4a0ee366e6ecedc4daafd01e83eee0720111ab294366fe7c185e935475ebcecd - languageName: node - linkType: hard - "supports-color@npm:^7.1.0": version: 7.2.0 resolution: "supports-color@npm:7.2.0" @@ -6770,15 +4615,6 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^8.1.1": - version: 8.1.1 - resolution: "supports-color@npm:8.1.1" - dependencies: - has-flag: "npm:^4.0.0" - checksum: 10c0/ea1d3c275dd604c974670f63943ed9bd83623edc102430c05adb8efc56ba492746b6e95386e7831b872ec3807fd89dd8eb43f735195f37b5ec343e4234cc7e89 - languageName: node - linkType: hard - "supports-preserve-symlinks-flag@npm:^1.0.0": version: 1.0.0 resolution: "supports-preserve-symlinks-flag@npm:1.0.0" @@ -6786,15 +4622,6 @@ __metadata: languageName: node linkType: hard -"synckit@npm:^0.11.8": - version: 0.11.13 - resolution: "synckit@npm:0.11.13" - dependencies: - "@pkgr/core": "npm:^0.3.6" - checksum: 10c0/5a6c19f4f79045aaa7994106401bff6dbe7cca23a6d0a0723ff14eb8b1bebeb4a71729118f6914905598e304ea2fa13509885e11ba07d92e7cb68a06740cb328 - languageName: node - linkType: hard - "tabbable@npm:^6.0.0": version: 6.5.0 resolution: "tabbable@npm:6.5.0" @@ -6836,17 +4663,6 @@ __metadata: languageName: node linkType: hard -"test-exclude@npm:^6.0.0": - version: 6.0.0 - resolution: "test-exclude@npm:6.0.0" - dependencies: - "@istanbuljs/schema": "npm:^0.1.2" - glob: "npm:^7.1.4" - minimatch: "npm:^3.0.4" - checksum: 10c0/019d33d81adff3f9f1bfcff18125fb2d3c65564f437d9be539270ee74b994986abb8260c7c2ce90e8f30162178b09dbbce33c6389273afac4f36069c48521f57 - languageName: node - linkType: hard - "tiny-emitter@npm:^2.1.0": version: 2.1.0 resolution: "tiny-emitter@npm:2.1.0" @@ -6885,13 +4701,6 @@ __metadata: languageName: node linkType: hard -"tmpl@npm:1.0.5": - version: 1.0.5 - resolution: "tmpl@npm:1.0.5" - checksum: 10c0/f935537799c2d1922cb5d6d3805f594388f75338fe7a4a9dac41504dd539704ca4db45b883b52e7b0aa5b2fd5ddadb1452bf95cd23a69da2f793a843f9451cc9 - languageName: node - linkType: hard - "to-regex-range@npm:^5.0.1": version: 5.0.1 resolution: "to-regex-range@npm:5.0.1" @@ -6910,46 +4719,6 @@ __metadata: languageName: node linkType: hard -"ts-jest@npm:^29.4.11": - version: 29.4.11 - resolution: "ts-jest@npm:29.4.11" - dependencies: - bs-logger: "npm:^0.2.6" - fast-json-stable-stringify: "npm:^2.1.0" - handlebars: "npm:^4.7.9" - json5: "npm:^2.2.3" - lodash.memoize: "npm:^4.1.2" - make-error: "npm:^1.3.6" - semver: "npm:^7.8.0" - type-fest: "npm:^4.41.0" - yargs-parser: "npm:^21.1.1" - peerDependencies: - "@babel/core": ">=7.0.0-beta.0 <8" - "@jest/transform": ^29.0.0 || ^30.0.0 - "@jest/types": ^29.0.0 || ^30.0.0 - babel-jest: ^29.0.0 || ^30.0.0 - jest: ^29.0.0 || ^30.0.0 - jest-util: ^29.0.0 || ^30.0.0 - typescript: ">=4.3 <7" - peerDependenciesMeta: - "@babel/core": - optional: true - "@jest/transform": - optional: true - "@jest/types": - optional: true - babel-jest: - optional: true - esbuild: - optional: true - jest-util: - optional: true - bin: - ts-jest: cli.js - checksum: 10c0/f9e6ab3235f33088c4d6441da97d4b03b1fe9c21f4d859d7acf3f325ea36471a6c1ea9301f445b2670efa1a391dcddc31ecd8641a2d673752d074136311b8480 - languageName: node - linkType: hard - "tslib@npm:^2.0.3, tslib@npm:^2.4.0, tslib@npm:^2.8.1": version: 2.8.1 resolution: "tslib@npm:2.8.1" @@ -6988,27 +4757,6 @@ __metadata: languageName: node linkType: hard -"type-detect@npm:4.0.8": - version: 4.0.8 - resolution: "type-detect@npm:4.0.8" - checksum: 10c0/8fb9a51d3f365a7de84ab7f73b653534b61b622aa6800aecdb0f1095a4a646d3f5eb295322127b6573db7982afcd40ab492d038cf825a42093a58b1e1353e0bd - languageName: node - linkType: hard - -"type-fest@npm:^0.21.3": - version: 0.21.3 - resolution: "type-fest@npm:0.21.3" - checksum: 10c0/902bd57bfa30d51d4779b641c2bc403cdf1371fb9c91d3c058b0133694fcfdb817aef07a47f40faf79039eecbaa39ee9d3c532deff244f3a19ce68cea71a61e8 - languageName: node - linkType: hard - -"type-fest@npm:^4.41.0": - version: 4.41.0 - resolution: "type-fest@npm:4.41.0" - checksum: 10c0/f5ca697797ed5e88d33ac8f1fec21921839871f808dc59345c9cf67345bfb958ce41bd821165dbf3ae591cedec2bf6fe8882098dfdd8dc54320b859711a2c1e4 - languageName: node - linkType: hard - "typed-function@npm:^4.2.1": version: 4.2.2 resolution: "typed-function@npm:4.2.2" @@ -7051,15 +4799,6 @@ __metadata: languageName: node linkType: hard -"uglify-js@npm:^3.1.4": - version: 3.19.3 - resolution: "uglify-js@npm:3.19.3" - bin: - uglifyjs: bin/uglifyjs - checksum: 10c0/83b0a90eca35f778e07cad9622b80c448b6aad457c9ff8e568afed978212b42930a95f9e1be943a1ffa4258a3340fbb899f41461131c05bb1d0a9c303aed8479 - languageName: node - linkType: hard - "undici-types@npm:>=7.24.0 <7.24.7": version: 7.24.6 resolution: "undici-types@npm:7.24.6" @@ -7067,13 +4806,6 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~8.3.0": - version: 8.3.0 - resolution: "undici-types@npm:8.3.0" - checksum: 10c0/c8aa7e2fbebfce519654dafadc0ece59be888d2ccaf180fb4495da875e7b536d2456345c384069c7e6f3e9c9ab7435f074957da306f142343eee86ff8048855a - languageName: node - linkType: hard - "undici@npm:^6.25.0": version: 6.27.0 resolution: "undici@npm:6.27.0" @@ -7088,82 +4820,6 @@ __metadata: languageName: node linkType: hard -"unrs-resolver@npm:^1.7.11": - version: 1.12.2 - resolution: "unrs-resolver@npm:1.12.2" - dependencies: - "@unrs/resolver-binding-android-arm-eabi": "npm:1.12.2" - "@unrs/resolver-binding-android-arm64": "npm:1.12.2" - "@unrs/resolver-binding-darwin-arm64": "npm:1.12.2" - "@unrs/resolver-binding-darwin-x64": "npm:1.12.2" - "@unrs/resolver-binding-freebsd-x64": "npm:1.12.2" - "@unrs/resolver-binding-linux-arm-gnueabihf": "npm:1.12.2" - "@unrs/resolver-binding-linux-arm-musleabihf": "npm:1.12.2" - "@unrs/resolver-binding-linux-arm64-gnu": "npm:1.12.2" - "@unrs/resolver-binding-linux-arm64-musl": "npm:1.12.2" - "@unrs/resolver-binding-linux-loong64-gnu": "npm:1.12.2" - "@unrs/resolver-binding-linux-loong64-musl": "npm:1.12.2" - "@unrs/resolver-binding-linux-ppc64-gnu": "npm:1.12.2" - "@unrs/resolver-binding-linux-riscv64-gnu": "npm:1.12.2" - "@unrs/resolver-binding-linux-riscv64-musl": "npm:1.12.2" - "@unrs/resolver-binding-linux-s390x-gnu": "npm:1.12.2" - "@unrs/resolver-binding-linux-x64-gnu": "npm:1.12.2" - "@unrs/resolver-binding-linux-x64-musl": "npm:1.12.2" - "@unrs/resolver-binding-openharmony-arm64": "npm:1.12.2" - "@unrs/resolver-binding-wasm32-wasi": "npm:1.12.2" - "@unrs/resolver-binding-win32-arm64-msvc": "npm:1.12.2" - "@unrs/resolver-binding-win32-ia32-msvc": "npm:1.12.2" - "@unrs/resolver-binding-win32-x64-msvc": "npm:1.12.2" - napi-postinstall: "npm:^0.3.4" - dependenciesMeta: - "@unrs/resolver-binding-android-arm-eabi": - optional: true - "@unrs/resolver-binding-android-arm64": - optional: true - "@unrs/resolver-binding-darwin-arm64": - optional: true - "@unrs/resolver-binding-darwin-x64": - optional: true - "@unrs/resolver-binding-freebsd-x64": - optional: true - "@unrs/resolver-binding-linux-arm-gnueabihf": - optional: true - "@unrs/resolver-binding-linux-arm-musleabihf": - optional: true - "@unrs/resolver-binding-linux-arm64-gnu": - optional: true - "@unrs/resolver-binding-linux-arm64-musl": - optional: true - "@unrs/resolver-binding-linux-loong64-gnu": - optional: true - "@unrs/resolver-binding-linux-loong64-musl": - optional: true - "@unrs/resolver-binding-linux-ppc64-gnu": - optional: true - "@unrs/resolver-binding-linux-riscv64-gnu": - optional: true - "@unrs/resolver-binding-linux-riscv64-musl": - optional: true - "@unrs/resolver-binding-linux-s390x-gnu": - optional: true - "@unrs/resolver-binding-linux-x64-gnu": - optional: true - "@unrs/resolver-binding-linux-x64-musl": - optional: true - "@unrs/resolver-binding-openharmony-arm64": - optional: true - "@unrs/resolver-binding-wasm32-wasi": - optional: true - "@unrs/resolver-binding-win32-arm64-msvc": - optional: true - "@unrs/resolver-binding-win32-ia32-msvc": - optional: true - "@unrs/resolver-binding-win32-x64-msvc": - optional: true - checksum: 10c0/ddc27f6d920eabdafeac0077ebff9fd799c895cea025751dc17b360bf9be7c93c471fafebf65f205eec476f90d7daa36aef889d47362b2dd4705d68852bcfea4 - languageName: node - linkType: hard - "update-browserslist-db@npm:^1.2.3": version: 1.2.3 resolution: "update-browserslist-db@npm:1.2.3" @@ -7205,17 +4861,6 @@ __metadata: languageName: node linkType: hard -"v8-to-istanbul@npm:^9.0.1": - version: 9.3.0 - resolution: "v8-to-istanbul@npm:9.3.0" - dependencies: - "@jridgewell/trace-mapping": "npm:^0.3.12" - "@types/istanbul-lib-coverage": "npm:^2.0.1" - convert-source-map: "npm:^2.0.0" - checksum: 10c0/968bcf1c7c88c04df1ffb463c179558a2ec17aa49e49376120504958239d9e9dad5281aa05f2a78542b8557f2be0b0b4c325710262f3b838b40d703d5ed30c23 - languageName: node - linkType: hard - "vite@npm:^6.0.0 || ^7.0.0 || ^8.0.0": version: 8.0.16 resolution: "vite@npm:8.0.16" @@ -7358,15 +5003,6 @@ __metadata: languageName: node linkType: hard -"walker@npm:^1.0.8": - version: 1.0.8 - resolution: "walker@npm:1.0.8" - dependencies: - makeerror: "npm:1.0.12" - checksum: 10c0/a17e037bccd3ca8a25a80cb850903facdfed0de4864bd8728f1782370715d679fa72e0a0f5da7c1c1379365159901e5935f35be531229da53bbfc0efdabdb48e - languageName: node - linkType: hard - "warning@npm:^4.0.2": version: 4.0.3 resolution: "warning@npm:4.0.3" @@ -7417,59 +5053,6 @@ __metadata: languageName: node linkType: hard -"wordwrap@npm:^1.0.0": - version: 1.0.0 - resolution: "wordwrap@npm:1.0.0" - checksum: 10c0/7ed2e44f3c33c5c3e3771134d2b0aee4314c9e49c749e37f464bf69f2bcdf0cbf9419ca638098e2717cff4875c47f56a007532f6111c3319f557a2ca91278e92 - languageName: node - linkType: hard - -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": - version: 7.0.0 - resolution: "wrap-ansi@npm:7.0.0" - dependencies: - ansi-styles: "npm:^4.0.0" - string-width: "npm:^4.1.0" - strip-ansi: "npm:^6.0.0" - checksum: 10c0/d15fc12c11e4cbc4044a552129ebc75ee3f57aa9c1958373a4db0292d72282f54373b536103987a4a7594db1ef6a4f10acf92978f79b98c49306a4b58c77d4da - languageName: node - linkType: hard - -"wrap-ansi@npm:^8.1.0": - version: 8.1.0 - resolution: "wrap-ansi@npm:8.1.0" - dependencies: - ansi-styles: "npm:^6.1.0" - string-width: "npm:^5.0.1" - strip-ansi: "npm:^7.0.1" - checksum: 10c0/138ff58a41d2f877eae87e3282c0630fc2789012fc1af4d6bd626eeb9a2f9a65ca92005e6e69a75c7b85a68479fe7443c7dbe1eb8fbaa681a4491364b7c55c60 - languageName: node - linkType: hard - -"wrappy@npm:1": - version: 1.0.2 - resolution: "wrappy@npm:1.0.2" - checksum: 10c0/56fece1a4018c6a6c8e28fbc88c87e0fbf4ea8fd64fc6c63b18f4acc4bd13e0ad2515189786dd2c30d3eec9663d70f4ecf699330002f8ccb547e4a18231fc9f0 - languageName: node - linkType: hard - -"write-file-atomic@npm:^5.0.1": - version: 5.0.1 - resolution: "write-file-atomic@npm:5.0.1" - dependencies: - imurmurhash: "npm:^0.1.4" - signal-exit: "npm:^4.0.1" - checksum: 10c0/e8c850a8e3e74eeadadb8ad23c9d9d63e4e792bd10f4836ed74189ef6e996763959f1249c5650e232f3c77c11169d239cbfc8342fc70f3fe401407d23810505d - languageName: node - linkType: hard - -"y18n@npm:^5.0.5": - version: 5.0.8 - resolution: "y18n@npm:5.0.8" - checksum: 10c0/4df2842c36e468590c3691c894bc9cdbac41f520566e76e24f59401ba7d8b4811eb1e34524d57e54bc6d864bcb66baab7ffd9ca42bf1eda596618f9162b91249 - languageName: node - linkType: hard - "yallist@npm:^3.0.2": version: 3.1.1 resolution: "yallist@npm:3.1.1" @@ -7484,28 +5067,6 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^21.1.1": - version: 21.1.1 - resolution: "yargs-parser@npm:21.1.1" - checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2 - languageName: node - linkType: hard - -"yargs@npm:^17.7.2": - version: 17.7.3 - resolution: "yargs@npm:17.7.3" - dependencies: - cliui: "npm:^8.0.1" - escalade: "npm:^3.1.1" - get-caller-file: "npm:^2.0.5" - require-directory: "npm:^2.1.1" - string-width: "npm:^4.2.3" - y18n: "npm:^5.0.5" - yargs-parser: "npm:^21.1.1" - checksum: 10c0/7a28572f7e785a57886e34fdbddb9b28756dec552e1453d5f6e7cdd00ad8721a4e8c4321d33683f5e61cacb36ad43258adbb48396b71ec4ed14abee0fc0d0c1f - languageName: node - linkType: hard - "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0" From e98dbcf60b19580375bd06d183b461008dfceb6d Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Thu, 6 Aug 2026 21:52:03 +0800 Subject: [PATCH 05/29] Update linting configuration to bring it inline with other repos --- .prettierignore | 5 ++++- .prettierrc | 8 +++++--- eslint.config.mjs | 48 +++++++++++++++++++++++++++++++---------------- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/.prettierignore b/.prettierignore index 612f7df..0ba268d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,4 @@ -.changeset/config.json \ No newline at end of file +.changeset/config.json +.yarn +**/dist +**/node_modules diff --git a/.prettierrc b/.prettierrc index ac5d019..cfb29ac 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,7 @@ { - "printWidth": 100, "arrowParens": "avoid", - "endOfLine": "auto" -} \ No newline at end of file + "endOfLine": "auto", + "printWidth": 100, + "semi": true, + "singleQuote": true +} diff --git a/eslint.config.mjs b/eslint.config.mjs index d0051ff..a08dc73 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,42 +1,58 @@ -import eslintConfigPrettierFlat from "eslint-config-prettier/flat"; -import { defineConfig } from "eslint/config"; -import tseslint from "typescript-eslint"; +// @ts-check + +import eslintConfigPrettierFlat from 'eslint-config-prettier/flat'; +import { defineConfig } from 'eslint/config'; +import tseslint from 'typescript-eslint'; +import vitest from '@vitest/eslint-plugin'; export default defineConfig([ { - ignores: ["**/dist", "**/node_modules", "coverage"], + ignores: ['**/dist', '**/node_modules', 'coverage'], }, tseslint.configs.recommended, eslintConfigPrettierFlat, { - files: ["src/**/*.{ts,tsx}"], + files: ['src/**/*.{ts,tsx}'], languageOptions: { parser: tseslint.parser, parserOptions: { projectService: { - allowDefaultProject: ['vitest.config.ts'] + allowDefaultProject: ['vitest.config.ts'], }, tsconfigRootDir: import.meta.dirname, }, }, plugins: { - "@typescript-eslint": tseslint.plugin, + '@typescript-eslint': tseslint.plugin, }, rules: { - "@typescript-eslint/no-namespace": "off", - "@typescript-eslint/ban-ts-comment": "warn", - "@typescript-eslint/no-import-type-side-effects": "error", - "@typescript-eslint/no-unnecessary-type-assertion": "error", - "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }], + '@typescript-eslint/no-namespace': 'off', + '@typescript-eslint/ban-ts-comment': 'warn', + '@typescript-eslint/no-import-type-side-effects': 'error', + '@typescript-eslint/no-unnecessary-type-assertion': 'error', + '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], '@typescript-eslint/only-throw-error': 'error', - "@typescript-eslint/require-await": "error", - "@typescript-eslint/return-await": ["error", "in-try-catch"], + '@typescript-eslint/require-await': 'error', + '@typescript-eslint/return-await': ['error', 'in-try-catch'], + }, + }, + { + files: ['**/__tests__/**/*.test.{ts,tsx}'], + extends: [vitest.configs.recommended], + plugins: { + vitest, + }, + rules: { + '@typescript-eslint/no-explicit-any': 'off', + + 'vitest/prefer-import-in-mock': 'error', + 'vitest/prefer-vi-mocked': 'error', }, }, { - files: ["src/**/*.js"], + files: ['src/**/*.js'], rules: { - "@typescript-eslint/no-require-imports": "off", + '@typescript-eslint/no-require-imports': 'off', }, }, ]); From 6670e33e80740380711f8fc0acd1b2dbfe26311b Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Thu, 6 Aug 2026 21:52:17 +0800 Subject: [PATCH 06/29] Run format --- .vscode/extensions.json | 6 +- .vscode/settings.json | 2 +- lib/build.ts | 56 ++--- package.json | 2 + src/common/autocomplete/rollup.config.mjs | 18 +- src/common/autocomplete/src/autocomplete.ts | 54 ++-- src/common/autocomplete/src/constants.ts | 8 +- src/common/autocomplete/src/index.ts | 6 +- src/common/autocomplete/src/syntax.ts | 8 +- src/common/cse-machine/rollup.config.mjs | 19 +- .../cse-machine/src/__tests__/common.test.ts | 192 +++++++-------- src/common/cse-machine/src/index.ts | 10 +- src/common/data-visualizer/package.json | 3 +- src/common/data-visualizer/rollup.config.mjs | 18 +- .../src/__tests__/common.test.ts | 10 +- src/common/data-visualizer/src/index.ts | 22 +- src/common/data-visualizer/tsconfig.json | 2 +- src/common/module-loader/rollup.config.mjs | 19 +- src/common/module-loader/src/index.ts | 12 +- src/common/stepper/rollup.config.mjs | 18 +- .../stepper/src/__tests__/common.test.ts | 10 +- src/common/stepper/src/index.ts | 18 +- src/common/stepper/tsconfig.json | 2 +- src/common/tabs/rollup.config.mjs | 19 +- src/common/tabs/src/index.ts | 4 +- src/common/test/rollup.config.mjs | 18 +- src/common/test/src/__tests__/common.test.ts | 8 +- src/common/test/src/index.ts | 14 +- src/runner/autocomplete/rollup.config.mjs | 19 +- .../autocomplete/src/__tests__/runner.test.ts | 80 +++--- src/runner/autocomplete/src/index.ts | 32 +-- src/runner/cse-machine/rollup.config.mjs | 18 +- .../cse-machine/src/__tests__/runner.test.ts | 55 ++--- src/runner/cse-machine/src/index.ts | 6 +- src/runner/data-visualizer/rollup.config.mjs | 19 +- .../src/__tests__/runner.test.ts | 86 +++---- src/runner/data-visualizer/src/index.ts | 12 +- src/runner/module-loader/rollup.config.mjs | 18 +- .../src/__tests__/runner.test.ts | 101 ++++---- src/runner/module-loader/src/index.ts | 8 +- src/runner/remoteExecution/index.ts | 12 +- src/runner/remoteExecution/rollup.config.mjs | 20 +- src/runner/remoteExecution/src/index.ts | 2 +- src/runner/stepper/rollup.config.mjs | 19 +- .../stepper/src/__tests__/runner.test.ts | 31 ++- src/runner/stepper/src/index.ts | 12 +- src/runner/stepper/tsconfig.json | 2 +- src/runner/test/rollup.config.mjs | 18 +- src/runner/test/src/__tests__/runner.test.ts | 34 +-- src/runner/test/src/index.ts | 6 +- src/web/autocomplete/rollup.config.mjs | 22 +- .../autocomplete/src/__tests__/web.test.ts | 144 +++++------ src/web/autocomplete/src/index.ts | 16 +- src/web/cse-machine/rollup.config.mjs | 18 +- src/web/cse-machine/src/__tests__/web.test.ts | 79 +++--- src/web/cse-machine/src/index.ts | 8 +- src/web/data-visualizer/package.json | 3 +- src/web/data-visualizer/rollup.config.mjs | 36 +-- src/web/data-visualizer/src/Config.ts | 4 +- .../src/DataVisualizerHostPlugin.tsx | 20 +- .../src/DataVisualizerView.tsx | 60 ++--- .../src/__tests__/DataVisualizerView.test.tsx | 173 ++++++------- .../src/__tests__/classify.test.ts | 56 ++--- .../src/__tests__/tree.test.ts | 233 +++++++++--------- .../src/__tests__/treeViews.test.ts | 147 +++++------ src/web/data-visualizer/src/classify.ts | 42 ++-- .../src/drawable/ArrayDrawable.tsx | 16 +- .../src/drawable/ArrowDrawable.tsx | 8 +- .../src/drawable/BackwardArrowDrawable.tsx | 6 +- .../data-visualizer/src/drawable/Drawable.ts | 10 +- .../src/drawable/FunctionDrawable.tsx | 6 +- .../src/drawable/NullDrawable.tsx | 6 +- src/web/data-visualizer/src/format.ts | 6 +- src/web/data-visualizer/src/index.ts | 2 +- .../src/tree/AlreadyParsedTreeNode.ts | 4 +- .../src/tree/ArrayTreeNode.tsx | 16 +- .../src/tree/BinaryTreeDrawer.tsx | 36 +-- .../src/tree/ClassicDrawer.tsx | 30 +-- .../data-visualizer/src/tree/DataTreeNode.tsx | 2 +- .../src/tree/DrawableTreeNode.tsx | 2 +- .../src/tree/FunctionTreeNode.tsx | 10 +- .../src/tree/GeneralTreeDrawer.tsx | 36 +-- src/web/data-visualizer/src/tree/Tree.ts | 30 +-- src/web/data-visualizer/src/tree/TreeNode.ts | 10 +- src/web/data-visualizer/wrap.mjs | 8 +- src/web/module-loader/rollup.config.mjs | 22 +- .../module-loader/src/__tests__/web.test.ts | 90 +++---- src/web/module-loader/src/index.ts | 16 +- src/web/stepper/rollup.config.mjs | 28 +-- src/web/stepper/src/StepperHostPlugin.tsx | 22 +- src/web/stepper/src/SubstVisualizer.tsx | 212 ++++++++-------- src/web/stepper/src/index.ts | 2 +- src/web/stepper/src/styles.ts | 6 +- src/web/stepper/wrap.mjs | 8 +- src/web/test/rollup.config.mjs | 23 +- src/web/test/src/index.tsx | 24 +- vitest.config.ts | 8 +- yarn.lock | 108 ++++++++ 98 files changed, 1586 insertions(+), 1476 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index daaa5ee..da487ff 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,3 @@ { - "recommendations": [ - "arcanis.vscode-zipfs", - "dbaeumer.vscode-eslint", - "esbenp.prettier-vscode" - ] + "recommendations": ["arcanis.vscode-zipfs", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 53d8c03..91f8972 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,5 +6,5 @@ "js/ts.tsdk.promptToUseWorkspaceVersion": true, "js/ts.tsdk.path": ".yarn/sdks/typescript/lib", "eslint.nodePath": ".yarn/sdks", - "prettier.prettierPath": ".yarn/sdks/prettier/index.cjs", + "prettier.prettierPath": ".yarn/sdks/prettier/index.cjs" } diff --git a/lib/build.ts b/lib/build.ts index aeeb305..d3f4acd 100644 --- a/lib/build.ts +++ b/lib/build.ts @@ -1,13 +1,13 @@ -import { Command } from "commander"; -import packageJSON from "../package.json" with { type: "json" }; -import fs, { readFile, writeFile } from "fs/promises"; -import { spawn } from "child_process"; -import pathlib from "path"; -import type { IPluginDefinition } from "@sourceacademy/plugin-directory/dist/types/IPluginDefinition.js"; -import process from "process"; +import { Command } from 'commander'; +import packageJSON from '../package.json' with { type: 'json' }; +import fs, { readFile, writeFile } from 'fs/promises'; +import { spawn } from 'child_process'; +import pathlib from 'path'; +import type { IPluginDefinition } from '@sourceacademy/plugin-directory/dist/types/IPluginDefinition.js'; +import process from 'process'; const program = new Command(); interface Manifest { - type: "installable" | "external"; + type: 'installable' | 'external'; } const globalManifest: Record> = {}; @@ -23,7 +23,7 @@ const pluginDirectory: Record = {}; */ async function generateManifest() { // Create dist directory if it doesn't exist - await fs.mkdir("dist", { recursive: true }); + await fs.mkdir('dist', { recursive: true }); // The ignore list is used to keep track of non-installable plugins, which should be ignored in changesets to prevent unnecessary version bumps and releases. const ignoreList: string[] = []; @@ -32,15 +32,15 @@ async function generateManifest() { packageJSON.workspaces.map(async workspace => { const manifest: Record = {}; // pluginType is either "web", "runner" or "common" - const pluginType = workspace.split("/").slice(-3)[0]; + const pluginType = workspace.split('/').slice(-3)[0]; for await (const file of fs.glob(`${workspace}manifest.json`)) { - const manifestFile = JSON.parse(await fs.readFile(file, "utf-8")); - const packageJSONPath = file.replace("manifest.json", "package.json"); - const packageJSONFile = JSON.parse(await fs.readFile(packageJSONPath, "utf-8")); + const manifestFile = JSON.parse(await fs.readFile(file, 'utf-8')); + const packageJSONPath = file.replace('manifest.json', 'package.json'); + const packageJSONFile = JSON.parse(await fs.readFile(packageJSONPath, 'utf-8')); // folderName is the name of the plugin folder (generally the same as the plugin name) const folderName = file.split(pathlib.sep).slice(-2)[0]; manifest[folderName] = manifestFile as Manifest; - if (manifestFile.type !== "installable") { + if (manifestFile.type !== 'installable') { ignoreList.push(`@sourceacademy/${pluginType}-${folderName}`); pluginDirectory[folderName] = pluginDirectory[folderName] || { id: folderName, @@ -54,12 +54,12 @@ async function generateManifest() { // package.json is untyped JSON, so guard against main/module being non-string (e.g. an // object, from a more complex export config) before handing them to pathlib.basename. const mainEntry = - typeof packageJSONFile.main === "string" ? packageJSONFile.main : undefined; + typeof packageJSONFile.main === 'string' ? packageJSONFile.main : undefined; const moduleEntry = - typeof packageJSONFile.module === "string" ? packageJSONFile.module : undefined; - const entryFile = pathlib.basename(mainEntry ?? moduleEntry ?? "index.js"); + typeof packageJSONFile.module === 'string' ? packageJSONFile.module : undefined; + const entryFile = pathlib.basename(mainEntry ?? moduleEntry ?? 'index.js'); pluginDirectory[folderName].resolutions[pluginType] = - "./" + pluginType + "/" + folderName + "/" + entryFile; + './' + pluginType + '/' + folderName + '/' + entryFile; } } globalManifest[pluginType] = manifest; @@ -74,10 +74,10 @@ async function generateManifest() { ); // Update changeset config to ignore non-installable plugins - const changesetConfig = await fs.readFile(".changeset/config.json", "utf-8"); + const changesetConfig = await fs.readFile('.changeset/config.json', 'utf-8'); const changesetConfigJSON = JSON.parse(changesetConfig); changesetConfigJSON.ignore = ignoreList; - await fs.writeFile(".changeset/config.json", JSON.stringify(changesetConfigJSON, null, 2)); + await fs.writeFile('.changeset/config.json', JSON.stringify(changesetConfigJSON, null, 2)); } /** @@ -90,10 +90,10 @@ async function generateManifest() { async function spawnPromise(...args: Parameters) { return new Promise((resolve, reject) => { const child = spawn(...args); - child.on("error", err => { + child.on('error', err => { reject(err); }); - child.on("close", code => { + child.on('close', code => { if (code === 0) { resolve(); } else { @@ -116,7 +116,7 @@ async function copyDistFiles() { await Promise.all( // Iterate over each plugin in the plugin type and copy the dist files if the plugin is of type "external" Object.entries(manifest).map(async ([pluginName, pluginManifest]) => { - if (pluginManifest.type === "external") { + if (pluginManifest.type === 'external') { await fs.cp( `src/${pluginType}/${pluginName}/dist`, `dist/${pluginType}/${pluginName}`, @@ -141,10 +141,10 @@ async function build(extraArgs: string[] = []) { await Promise.all([ generateManifest(), spawnPromise( - process.platform === "win32" ? "yarn.cmd" : "yarn", - ["workspaces", "foreach", "-Apt", "--topological-dev", "run", "build", ...extraArgs], + process.platform === 'win32' ? 'yarn.cmd' : 'yarn', + ['workspaces', 'foreach', '-Apt', '--topological-dev', 'run', 'build', ...extraArgs], { - stdio: "inherit", + stdio: 'inherit', shell: true, }, ), @@ -157,10 +157,10 @@ async function build(extraArgs: string[] = []) { * Transforms a particular external plugin to its final form */ async function transformSingle(path: string) { - if (path.endsWith("mjs")) { + if (path.endsWith('mjs')) { return; } - let file = (await readFile(path)).toString("utf-8"); + let file = (await readFile(path)).toString('utf-8'); // Create a mock "module" object, then return the exports file = `export default require => {let module = {exports: {}}; let exports = module.exports; ${file}; return module.exports;}`; diff --git a/package.json b/package.json index ab5e898..5002c86 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "scripts": { "build": "tsx lib/build.ts", "dev": "tsx lib/build.ts --watch", + "runall": "yarn workspaces foreach -A --exclude \"@sourceacademy/plugins-workspace\" run", "test": "vitest run", "test-coverage": "vitest run --coverage", "test:dev": "vitest", @@ -24,6 +25,7 @@ "@sourceacademy/plugin-directory": "source-academy/plugin-directory#main", "@types/node": "^25.9.1", "@vitest/coverage-istanbul": "^4.1.9", + "@vitest/eslint-plugin": "^1.6.26", "@yarnpkg/types": "^4.0.1", "commander": "^15.0.0", "eslint": "^10.0.3", diff --git a/src/common/autocomplete/rollup.config.mjs b/src/common/autocomplete/rollup.config.mjs index 0d387b7..9a8d5e0 100644 --- a/src/common/autocomplete/rollup.config.mjs +++ b/src/common/autocomplete/rollup.config.mjs @@ -1,20 +1,20 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), typescript(), terser()], diff --git a/src/common/autocomplete/src/autocomplete.ts b/src/common/autocomplete/src/autocomplete.ts index cfc5031..06b4950 100644 --- a/src/common/autocomplete/src/autocomplete.ts +++ b/src/common/autocomplete/src/autocomplete.ts @@ -1,34 +1,34 @@ // Adapted from https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.languageserver.protocol.completionitemkind?view=visualstudiosdk-2022 export enum CompletionItemKind { - Text = "text", - Method = "method", - Function = "func", - Constructor = "constructor", - Field = "field", - Variable = "var", - Class = "class", - Interface = "interface", - Module = "module", - Property = "property", - Unit = "unit", - Value = "value", - Enum = "enum", - Keyword = "keyword", - Snippet = "snippet", - Color = "color", - File = "file", - Reference = "reference", - Folder = "folder", - EnumMember = "enumMember", - Constant = "constant", - Struct = "struct", - Event = "event", - Operator = "operator", - TypeParameter = "typeParameter", + Text = 'text', + Method = 'method', + Function = 'func', + Constructor = 'constructor', + Field = 'field', + Variable = 'var', + Class = 'class', + Interface = 'interface', + Module = 'module', + Property = 'property', + Unit = 'unit', + Value = 'value', + Enum = 'enum', + Keyword = 'keyword', + Snippet = 'snippet', + Color = 'color', + File = 'file', + Reference = 'reference', + Folder = 'folder', + EnumMember = 'enumMember', + Constant = 'constant', + Struct = 'struct', + Event = 'event', + Operator = 'operator', + TypeParameter = 'typeParameter', } export interface AutoCompleteRequest { - type: "request"; + type: 'request'; /** Correlates this request with its response. */ requestId: number; /** The current code in the editor */ @@ -40,7 +40,7 @@ export interface AutoCompleteRequest { } export interface AutoCompleteResponse { - type: "response"; + type: 'response'; /** The ID of the request that produced this response. */ requestId: number; /** A list of autocomplete entries relevant to the current cursor position in the code */ diff --git a/src/common/autocomplete/src/constants.ts b/src/common/autocomplete/src/constants.ts index bc0faa2..5c90a51 100644 --- a/src/common/autocomplete/src/constants.ts +++ b/src/common/autocomplete/src/constants.ts @@ -1,5 +1,5 @@ -export const WEB_PLUGIN_ID = "__autocomplete_plugin_web"; -export const RUNNER_PLUGIN_ID = "__autocomplete_plugin_runner"; +export const WEB_PLUGIN_ID = '__autocomplete_plugin_web'; +export const RUNNER_PLUGIN_ID = '__autocomplete_plugin_runner'; -export const AUTOCOMPLETE_CHANNEL_ID = "__autocomplete_channel__"; -export const SYNTAX_CHANNEL_ID = "__syntax_channel__"; +export const AUTOCOMPLETE_CHANNEL_ID = '__autocomplete_channel__'; +export const SYNTAX_CHANNEL_ID = '__syntax_channel__'; diff --git a/src/common/autocomplete/src/index.ts b/src/common/autocomplete/src/index.ts index 5de3ef1..f4538a3 100644 --- a/src/common/autocomplete/src/index.ts +++ b/src/common/autocomplete/src/index.ts @@ -1,3 +1,3 @@ -export * from "./autocomplete"; -export * from "./constants"; -export * from "./syntax"; +export * from './autocomplete'; +export * from './constants'; +export * from './syntax'; diff --git a/src/common/autocomplete/src/syntax.ts b/src/common/autocomplete/src/syntax.ts index bdef6f0..30bdc74 100644 --- a/src/common/autocomplete/src/syntax.ts +++ b/src/common/autocomplete/src/syntax.ts @@ -1,12 +1,12 @@ export interface SyntaxHighlightRequest { - type: "request"; + type: 'request'; } export interface SyntaxHighlightResponse { - type: "response"; + type: 'response'; data: TransferredSyntaxHighlightData; } export interface SyntaxHighlightAck { - type: "ack"; + type: 'ack'; } export type SyntaxHighlightMessage = @@ -86,7 +86,7 @@ export interface SyntaxHighlightData { */ export type TransferredSyntaxHighlightData = Omit< SyntaxHighlightData, - "indents" | "outdents" | "autoOutdent" + 'indents' | 'outdents' | 'autoOutdent' > & { indents: TransferredModeFunction; outdents: TransferredModeFunction; diff --git a/src/common/cse-machine/rollup.config.mjs b/src/common/cse-machine/rollup.config.mjs index 58838a5..9a8d5e0 100644 --- a/src/common/cse-machine/rollup.config.mjs +++ b/src/common/cse-machine/rollup.config.mjs @@ -1,22 +1,21 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), typescript(), terser()], }); - diff --git a/src/common/cse-machine/src/__tests__/common.test.ts b/src/common/cse-machine/src/__tests__/common.test.ts index fa012f3..9595e5d 100644 --- a/src/common/cse-machine/src/__tests__/common.test.ts +++ b/src/common/cse-machine/src/__tests__/common.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect } from "vitest"; +import { describe, it, expect } from 'vitest'; import { CSE_CHANNEL, CSE_DIRECTORY_ID, @@ -11,81 +11,81 @@ import { type CseSerializedInstruction, type CseSerializedEnvFrame, type CseSerializedBinding, -} from "../index"; +} from '../index'; // ── Constants ───────────────────────────────────────────────────────────────── -describe("constants", () => { - it("CSE_CHANNEL has the expected value", () => { - expect(CSE_CHANNEL).toBe("__cse"); +describe('constants', () => { + it('CSE_CHANNEL has the expected value', () => { + expect(CSE_CHANNEL).toBe('__cse'); }); - it("RUNNER_ID has the expected value", () => { - expect(RUNNER_ID).toBe("__runner_cse"); + it('RUNNER_ID has the expected value', () => { + expect(RUNNER_ID).toBe('__runner_cse'); }); - it("WEB_ID has the expected value", () => { - expect(WEB_ID).toBe("__web_cse"); + it('WEB_ID has the expected value', () => { + expect(WEB_ID).toBe('__web_cse'); }); - it("CSE_DIRECTORY_ID has the expected value", () => { - expect(CSE_DIRECTORY_ID).toBe("cse-machine"); + it('CSE_DIRECTORY_ID has the expected value', () => { + expect(CSE_DIRECTORY_ID).toBe('cse-machine'); }); - it("CSE_MESSAGE_TYPE_SNAPSHOTS has the expected value", () => { - expect(CSE_MESSAGE_TYPE_SNAPSHOTS).toBe("snapshots"); + it('CSE_MESSAGE_TYPE_SNAPSHOTS has the expected value', () => { + expect(CSE_MESSAGE_TYPE_SNAPSHOTS).toBe('snapshots'); }); - it("RUNNER_ID and WEB_ID are distinct", () => { + it('RUNNER_ID and WEB_ID are distinct', () => { expect(RUNNER_ID).not.toBe(WEB_ID); }); }); // ── Structured-clone safety ─────────────────────────────────────────────────── -describe("structured-clone safety", () => { - it("a minimal snapshot survives structuredClone", () => { +describe('structured-clone safety', () => { + it('a minimal snapshot survives structuredClone', () => { const snap: CseSnapshot = { stepIndex: 0, control: [], stash: [], - environments: [{ id: "g", name: "global", parentId: null, bindings: [], isActive: true }], + environments: [{ id: 'g', name: 'global', parentId: null, bindings: [], isActive: true }], }; expect(structuredClone(snap)).toEqual(snap); }); - it("a fully-populated snapshot survives structuredClone", () => { + it('a fully-populated snapshot survives structuredClone', () => { const snap: CseSnapshot = { stepIndex: 2, control: [ - { displayText: "call f", tag: "app", metadata: { instrType: "Application", numOfArgs: 1 } }, + { displayText: 'call f', tag: 'app', metadata: { instrType: 'Application', numOfArgs: 1 } }, ], - stash: [{ displayValue: "42", label: "number", tag: "int", metadata: { raw: 42 } }], + stash: [{ displayValue: '42', label: 'number', tag: 'int', metadata: { raw: 42 } }], environments: [ { - id: "e1", - name: "f", - parentId: "g", - bindings: [{ name: "x", value: { displayValue: "1", label: "number" }, isConst: true }], - heapObjects: [{ displayValue: "", label: "closure" }], + id: 'e1', + name: 'f', + parentId: 'g', + bindings: [{ name: 'x', value: { displayValue: '1', label: 'number' }, isConst: true }], + heapObjects: [{ displayValue: '', label: 'closure' }], isActive: true, isOnCallStack: true, - closureFrameId: "g", - globalNames: ["x"], + closureFrameId: 'g', + globalNames: ['x'], }, - { id: "g", name: "global", parentId: null, bindings: [], isActive: false }, + { id: 'g', name: 'global', parentId: null, bindings: [], isActive: false }, ], currentLine: 3, }; expect(structuredClone(snap)).toEqual(snap); }); - it("a CseSnapshotMessage with multiple snapshots survives structuredClone", () => { + it('a CseSnapshotMessage with multiple snapshots survives structuredClone', () => { const msg: CseSnapshotMessage = { type: CSE_MESSAGE_TYPE_SNAPSHOTS, snapshots: [ { stepIndex: 0, control: [], stash: [], environments: [], currentLine: 1 }, - { stepIndex: 1, control: [{ displayText: "pop" }], stash: [], environments: [] }, + { stepIndex: 1, control: [{ displayText: 'pop' }], stash: [], environments: [] }, ], totalSteps: 2, breakpointSteps: [1], @@ -96,8 +96,8 @@ describe("structured-clone safety", () => { // ── CseSnapshotMessage ──────────────────────────────────────────────────────── -describe("CseSnapshotMessage", () => { - it("totalSteps matches snapshots.length for a batch", () => { +describe('CseSnapshotMessage', () => { + it('totalSteps matches snapshots.length for a batch', () => { const snapshots: CseSnapshot[] = [ { stepIndex: 0, control: [], stash: [], environments: [] }, { stepIndex: 1, control: [], stash: [], environments: [] }, @@ -113,7 +113,7 @@ describe("CseSnapshotMessage", () => { expect(msg.snapshots.length).toBe(msg.totalSteps); }); - it("accepts an empty snapshots array", () => { + it('accepts an empty snapshots array', () => { const msg: CseSnapshotMessage = { type: CSE_MESSAGE_TYPE_SNAPSHOTS, snapshots: [], @@ -124,7 +124,7 @@ describe("CseSnapshotMessage", () => { expect(msg.totalSteps).toBe(0); }); - it("type discriminator is CSE_MESSAGE_TYPE_SNAPSHOTS", () => { + it('type discriminator is CSE_MESSAGE_TYPE_SNAPSHOTS', () => { const msg: CseSnapshotMessage = { type: CSE_MESSAGE_TYPE_SNAPSHOTS, snapshots: [], @@ -134,10 +134,10 @@ describe("CseSnapshotMessage", () => { expect(msg.type).toBe(CSE_MESSAGE_TYPE_SNAPSHOTS); }); - it("breakpointSteps carries the step indices where a breakpoint sits on top of the control", () => { + it('breakpointSteps carries the step indices where a breakpoint sits on top of the control', () => { const snapshots: CseSnapshot[] = [ { stepIndex: 0, control: [], stash: [], environments: [] }, - { stepIndex: 1, control: [{ displayText: "breakpoint()" }], stash: [], environments: [] }, + { stepIndex: 1, control: [{ displayText: 'breakpoint()' }], stash: [], environments: [] }, { stepIndex: 2, control: [], stash: [], environments: [] }, ]; const msg: CseSnapshotMessage = { @@ -149,7 +149,7 @@ describe("CseSnapshotMessage", () => { expect(msg.breakpointSteps).toEqual([1]); }); - it("breakpointSteps may be empty when no breakpoint was hit", () => { + it('breakpointSteps may be empty when no breakpoint was hit', () => { const msg: CseSnapshotMessage = { type: CSE_MESSAGE_TYPE_SNAPSHOTS, snapshots: [], @@ -162,8 +162,8 @@ describe("CseSnapshotMessage", () => { // ── CseSnapshot optional fields ─────────────────────────────────────────────── -describe("CseSnapshot optional fields", () => { - it("currentLine is optional and may be omitted", () => { +describe('CseSnapshot optional fields', () => { + it('currentLine is optional and may be omitted', () => { const snap: CseSnapshot = { stepIndex: 0, control: [], @@ -173,7 +173,7 @@ describe("CseSnapshot optional fields", () => { expect(snap.currentLine).toBeUndefined(); }); - it("currentLine is preserved when set", () => { + it('currentLine is preserved when set', () => { const snap: CseSnapshot = { stepIndex: 5, control: [], @@ -184,7 +184,7 @@ describe("CseSnapshot optional fields", () => { expect(snap.currentLine).toBe(12); }); - it("stepIndex is preserved across multiple steps", () => { + it('stepIndex is preserved across multiple steps', () => { const indices = [0, 1, 2, 10, 99]; const steps = indices.map(i => ({ stepIndex: i, @@ -198,41 +198,41 @@ describe("CseSnapshot optional fields", () => { // ── CseSerializedValue ──────────────────────────────────────────────────────── -describe("CseSerializedValue", () => { - it("accepts minimal value with displayValue and label only", () => { - const v: CseSerializedValue = { displayValue: "true", label: "bool" }; +describe('CseSerializedValue', () => { + it('accepts minimal value with displayValue and label only', () => { + const v: CseSerializedValue = { displayValue: 'true', label: 'bool' }; expect(v.tag).toBeUndefined(); expect(v.metadata).toBeUndefined(); }); - it("accepts value with all optional fields", () => { + it('accepts value with all optional fields', () => { const v: CseSerializedValue = { - displayValue: "", - label: "closure", - tag: "fn", - metadata: { closureFrameId: "e1", params: ["x", "y"] }, + displayValue: '', + label: 'closure', + tag: 'fn', + metadata: { closureFrameId: 'e1', params: ['x', 'y'] }, }; - expect(v.displayValue).toBe(""); - expect(v.metadata).toEqual({ closureFrameId: "e1", params: ["x", "y"] }); + expect(v.displayValue).toBe(''); + expect(v.metadata).toEqual({ closureFrameId: 'e1', params: ['x', 'y'] }); }); }); // ── CseSerializedInstruction ────────────────────────────────────────────────── -describe("CseSerializedInstruction", () => { - it("accepts minimal instruction with displayText only", () => { - const instr: CseSerializedInstruction = { displayText: "pop" }; +describe('CseSerializedInstruction', () => { + it('accepts minimal instruction with displayText only', () => { + const instr: CseSerializedInstruction = { displayText: 'pop' }; expect(instr.tag).toBeUndefined(); expect(instr.metadata).toBeUndefined(); }); - it("accepts instruction with metadata for animation dispatch", () => { + it('accepts instruction with metadata for animation dispatch', () => { const instr: CseSerializedInstruction = { - displayText: "call f", - metadata: { instrType: "Application", numOfArgs: 2, startLine: 4, endLine: 4 }, + displayText: 'call f', + metadata: { instrType: 'Application', numOfArgs: 2, startLine: 4, endLine: 4 }, }; expect(instr.metadata).toEqual({ - instrType: "Application", + instrType: 'Application', numOfArgs: 2, startLine: 4, endLine: 4, @@ -242,11 +242,11 @@ describe("CseSerializedInstruction", () => { // ── CseSerializedEnvFrame ───────────────────────────────────────────────────── -describe("CseSerializedEnvFrame", () => { - it("root frame has null parentId", () => { +describe('CseSerializedEnvFrame', () => { + it('root frame has null parentId', () => { const frame: CseSerializedEnvFrame = { - id: "g", - name: "global", + id: 'g', + name: 'global', parentId: null, bindings: [], isActive: true, @@ -254,21 +254,21 @@ describe("CseSerializedEnvFrame", () => { expect(frame.parentId).toBeNull(); }); - it("child frame references parent by id", () => { + it('child frame references parent by id', () => { const frame: CseSerializedEnvFrame = { - id: "e1", - name: "f", - parentId: "g", + id: 'e1', + name: 'f', + parentId: 'g', bindings: [], isActive: true, }; - expect(frame.parentId).toBe("g"); + expect(frame.parentId).toBe('g'); }); - it("optional fields are absent when not set", () => { + it('optional fields are absent when not set', () => { const frame: CseSerializedEnvFrame = { - id: "g", - name: "global", + id: 'g', + name: 'global', parentId: null, bindings: [], isActive: false, @@ -279,67 +279,67 @@ describe("CseSerializedEnvFrame", () => { expect(frame.globalNames).toBeUndefined(); }); - it("globalNames carries names that resolve via the global frame instead of the enclosing-scope chain", () => { + it('globalNames carries names that resolve via the global frame instead of the enclosing-scope chain', () => { const frame: CseSerializedEnvFrame = { - id: "e1", - name: "f", - parentId: "g", + id: 'e1', + name: 'f', + parentId: 'g', bindings: [], isActive: true, - globalNames: ["x", "y"], + globalNames: ['x', 'y'], }; - expect(frame.globalNames).toEqual(["x", "y"]); + expect(frame.globalNames).toEqual(['x', 'y']); }); - it("heapObjects carries anonymous closures not bound to a name", () => { + it('heapObjects carries anonymous closures not bound to a name', () => { const frame: CseSerializedEnvFrame = { - id: "g", - name: "global", + id: 'g', + name: 'global', parentId: null, bindings: [], isActive: true, - heapObjects: [{ displayValue: "", label: "closure" }], + heapObjects: [{ displayValue: '', label: 'closure' }], }; expect(frame.heapObjects).toHaveLength(1); }); - it("closureFrameId links a frame to its defining environment", () => { + it('closureFrameId links a frame to its defining environment', () => { const frame: CseSerializedEnvFrame = { - id: "e2", - name: "lambda", - parentId: "g", + id: 'e2', + name: 'lambda', + parentId: 'g', bindings: [], isActive: false, - closureFrameId: "g", + closureFrameId: 'g', }; - expect(frame.closureFrameId).toBe("g"); + expect(frame.closureFrameId).toBe('g'); }); }); // ── CseSerializedBinding ────────────────────────────────────────────────────── -describe("CseSerializedBinding", () => { - it("isConst is optional and defaults to undefined", () => { +describe('CseSerializedBinding', () => { + it('isConst is optional and defaults to undefined', () => { const b: CseSerializedBinding = { - name: "x", - value: { displayValue: "5", label: "number" }, + name: 'x', + value: { displayValue: '5', label: 'number' }, }; expect(b.isConst).toBeUndefined(); }); - it("marks const bindings explicitly", () => { + it('marks const bindings explicitly', () => { const b: CseSerializedBinding = { - name: "PI", - value: { displayValue: "3.14159", label: "number" }, + name: 'PI', + value: { displayValue: '3.14159', label: 'number' }, isConst: true, }; expect(b.isConst).toBe(true); }); - it("marks mutable bindings explicitly", () => { + it('marks mutable bindings explicitly', () => { const b: CseSerializedBinding = { - name: "counter", - value: { displayValue: "0", label: "number" }, + name: 'counter', + value: { displayValue: '0', label: 'number' }, isConst: false, }; expect(b.isConst).toBe(false); diff --git a/src/common/cse-machine/src/index.ts b/src/common/cse-machine/src/index.ts index 300db4c..ffec741 100644 --- a/src/common/cse-machine/src/index.ts +++ b/src/common/cse-machine/src/index.ts @@ -17,22 +17,22 @@ */ /** The channel the CSE machine runner and host plugins communicate over. */ -export const CSE_CHANNEL = "__cse"; +export const CSE_CHANNEL = '__cse'; /** The id of the runner (worker-side) CSE machine plugin. */ -export const RUNNER_ID = "__runner_cse"; +export const RUNNER_ID = '__runner_cse'; /** The id of the web/host (browser-side) CSE machine plugin. */ -export const WEB_ID = "__web_cse"; +export const WEB_ID = '__web_cse'; /** * The id used to look the CSE machine up in the plugin directory (i.e. the argument to * `IRunnerPlugin.hostLoadPlugin`). The host resolves this to the web plugin's bundle URL. */ -export const CSE_DIRECTORY_ID = "cse-machine"; +export const CSE_DIRECTORY_ID = 'cse-machine'; /** The {@link CseSnapshotMessage.type} discriminator for a batch of snapshots. */ -export const CSE_MESSAGE_TYPE_SNAPSHOTS = "snapshots"; +export const CSE_MESSAGE_TYPE_SNAPSHOTS = 'snapshots'; /** * A single value on the stash or bound in an environment, serialized to plain JSON. diff --git a/src/common/data-visualizer/package.json b/src/common/data-visualizer/package.json index 4cc72f1..70497af 100644 --- a/src/common/data-visualizer/package.json +++ b/src/common/data-visualizer/package.json @@ -29,7 +29,8 @@ "@rollup/plugin-typescript": "catalog:", "rollup": "catalog:", "tslib": "catalog:", - "typescript": "catalog:" + "typescript": "catalog:", + "vitest": "catalog:" }, "repository": { "type": "git", diff --git a/src/common/data-visualizer/rollup.config.mjs b/src/common/data-visualizer/rollup.config.mjs index 0d387b7..9a8d5e0 100644 --- a/src/common/data-visualizer/rollup.config.mjs +++ b/src/common/data-visualizer/rollup.config.mjs @@ -1,20 +1,20 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), typescript(), terser()], diff --git a/src/common/data-visualizer/src/__tests__/common.test.ts b/src/common/data-visualizer/src/__tests__/common.test.ts index 09a17bf..2e30c92 100644 --- a/src/common/data-visualizer/src/__tests__/common.test.ts +++ b/src/common/data-visualizer/src/__tests__/common.test.ts @@ -1,11 +1,11 @@ -import { expect, test } from "vitest"; +import { expect, test } from 'vitest'; -import { DATA_VISUALIZER_CHANNEL_ID, RUNNER_ID, WEB_ID } from ".."; +import { DATA_VISUALIZER_CHANNEL_ID, RUNNER_ID, WEB_ID } from '..'; -test("runner and web ids are distinct", () => { +test('runner and web ids are distinct', () => { expect(RUNNER_ID).not.toBe(WEB_ID); }); -test("has a stable channel id", () => { - expect(DATA_VISUALIZER_CHANNEL_ID).toBe("__data_visualizer"); +test('has a stable channel id', () => { + expect(DATA_VISUALIZER_CHANNEL_ID).toBe('__data_visualizer'); }); diff --git a/src/common/data-visualizer/src/index.ts b/src/common/data-visualizer/src/index.ts index 0490323..d3abfbc 100644 --- a/src/common/data-visualizer/src/index.ts +++ b/src/common/data-visualizer/src/index.ts @@ -20,19 +20,19 @@ */ /** The channel the data visualizer runner and host plugins communicate over. */ -export const DATA_VISUALIZER_CHANNEL_ID = "__data_visualizer"; +export const DATA_VISUALIZER_CHANNEL_ID = '__data_visualizer'; /** The id of the runner (worker-side) data visualizer plugin. */ -export const RUNNER_ID = "__runner_data_visualizer"; +export const RUNNER_ID = '__runner_data_visualizer'; /** The id of the web/host (browser-side) data visualizer plugin. */ -export const WEB_ID = "__web_data_visualizer"; +export const WEB_ID = '__web_data_visualizer'; /** * The id used to look the data visualizer up in the plugin directory (i.e. the argument to * `IRunnerPlugin.hostLoadPlugin`). The host resolves this to the web plugin's bundle URL. */ -export const DATA_VISUALIZER_DIRECTORY_ID = "data-visualizer"; +export const DATA_VISUALIZER_DIRECTORY_ID = 'data-visualizer'; /** * Identifies one distinct compound value (a pair/array or a function) within a single top-level @@ -59,11 +59,11 @@ export type RefId = number; * distinguishes "pair" from "list" for classification purposes by `children.length`, not by tag. */ export type SerializedDataVisualizerNode = - | { type: "array"; refId: RefId; children: SerializedDataVisualizerNode[] } - | { type: "empty" } - | { type: "leaf"; displayValue: string; label: string } - | { type: "function"; refId: RefId; displayValue: string } - | { type: "ref"; refId: RefId }; + | { type: 'array'; refId: RefId; children: SerializedDataVisualizerNode[] } + | { type: 'empty' } + | { type: 'leaf'; displayValue: string; label: string } + | { type: 'function'; refId: RefId; displayValue: string } + | { type: 'ref'; refId: RefId }; /** One row = the fully-serialized arguments of a single `draw_data(...)` call. */ export type SerializedDataVisualizerRow = SerializedDataVisualizerNode[]; @@ -78,7 +78,7 @@ export type SerializedDataVisualizerRow = SerializedDataVisualizerNode[]; * (e.g. because the tab was closed) is always brought fully up to date by the next one. */ export interface DataVisualizerRowsMessage { - type: "rows"; + type: 'rows'; rows: SerializedDataVisualizerRow[]; } @@ -87,7 +87,7 @@ export interface DataVisualizerRowsMessage { * display when the data visualizer tab is (re)opened without re-running the program. */ export interface DataVisualizerRequestMessage { - type: "request"; + type: 'request'; } /** Every message that may cross the {@link DATA_VISUALIZER_CHANNEL_ID} channel. */ diff --git a/src/common/data-visualizer/tsconfig.json b/src/common/data-visualizer/tsconfig.json index b71f4ee..c7482f9 100644 --- a/src/common/data-visualizer/tsconfig.json +++ b/src/common/data-visualizer/tsconfig.json @@ -5,6 +5,6 @@ "compilerOptions": { "declaration": true, "outDir": "./dist", - "rootDir": "./src", + "rootDir": "./src" } } diff --git a/src/common/module-loader/rollup.config.mjs b/src/common/module-loader/rollup.config.mjs index 58838a5..9a8d5e0 100644 --- a/src/common/module-loader/rollup.config.mjs +++ b/src/common/module-loader/rollup.config.mjs @@ -1,22 +1,21 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), typescript(), terser()], }); - diff --git a/src/common/module-loader/src/index.ts b/src/common/module-loader/src/index.ts index 3ca4f9d..d6e3776 100644 --- a/src/common/module-loader/src/index.ts +++ b/src/common/module-loader/src/index.ts @@ -1,12 +1,12 @@ -export const WEB_ID = "__web_module_loader"; -export const RUNNER_ID = "__runner_module_loader"; +export const WEB_ID = '__web_module_loader'; +export const RUNNER_ID = '__runner_module_loader'; -export const CHANNEL_ID = "module_config"; +export const CHANNEL_ID = 'module_config'; export enum ModuleLoaderMessageType { - REQUEST_MODULE = "request_module", - MODULE_RESPONSE = "module_response", - MODULE_ERROR = "module_error", + REQUEST_MODULE = 'request_module', + MODULE_RESPONSE = 'module_response', + MODULE_ERROR = 'module_error', } export type ModuleLoaderMessage = diff --git a/src/common/stepper/rollup.config.mjs b/src/common/stepper/rollup.config.mjs index 0d387b7..9a8d5e0 100644 --- a/src/common/stepper/rollup.config.mjs +++ b/src/common/stepper/rollup.config.mjs @@ -1,20 +1,20 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), typescript(), terser()], diff --git a/src/common/stepper/src/__tests__/common.test.ts b/src/common/stepper/src/__tests__/common.test.ts index 766b5d8..819cc8f 100644 --- a/src/common/stepper/src/__tests__/common.test.ts +++ b/src/common/stepper/src/__tests__/common.test.ts @@ -1,11 +1,11 @@ -import { expect, test } from "vitest"; +import { expect, test } from 'vitest'; -import { RUNNER_ID, STEPPER_CHANNEL_ID, WEB_ID } from ".."; +import { RUNNER_ID, STEPPER_CHANNEL_ID, WEB_ID } from '..'; -test("runner and web ids are distinct", () => { +test('runner and web ids are distinct', () => { expect(RUNNER_ID).not.toBe(WEB_ID); }); -test("has a stable channel id", () => { - expect(STEPPER_CHANNEL_ID).toBe("__stepper"); +test('has a stable channel id', () => { + expect(STEPPER_CHANNEL_ID).toBe('__stepper'); }); diff --git a/src/common/stepper/src/index.ts b/src/common/stepper/src/index.ts index d7e5f42..234f6bb 100644 --- a/src/common/stepper/src/index.ts +++ b/src/common/stepper/src/index.ts @@ -12,19 +12,19 @@ */ /** The channel the stepper runner and host plugins communicate over. */ -export const STEPPER_CHANNEL_ID = "__stepper"; +export const STEPPER_CHANNEL_ID = '__stepper'; /** The id of the runner (worker-side) stepper plugin. */ -export const RUNNER_ID = "__runner_stepper"; +export const RUNNER_ID = '__runner_stepper'; /** The id of the web/host (browser-side) stepper plugin. */ -export const WEB_ID = "__web_stepper"; +export const WEB_ID = '__web_stepper'; /** * The id used to look the stepper up in the plugin directory (i.e. the argument to * `IRunnerPlugin.hostLoadPlugin`). The host resolves this to the web plugin's bundle URL. */ -export const STEPPER_DIRECTORY_ID = "stepper"; +export const STEPPER_DIRECTORY_ID = 'stepper'; /** * A single AST node, serialized to plain JSON. @@ -61,7 +61,7 @@ export interface SerializedMarker { */ redexNodeType?: string; /** Whether the highlight applies before or after the reduction. */ - redexType?: "beforeMarker" | "afterMarker"; + redexType?: 'beforeMarker' | 'afterMarker'; /** A human-readable explanation of the reduction, shown alongside the step. */ explanation?: string; } @@ -100,7 +100,7 @@ export interface SerializedStepperStep { */ /** A CSS class hint for a rendered token, mapped by the host to its stepper colour classes. */ -export type StepperTokenClass = "operator" | "identifier" | "literal" | "conditional"; +export type StepperTokenClass = 'operator' | 'identifier' | 'literal' | 'conditional'; /** * One piece of a node's render template. A template is an ordered list of parts; the host emits each @@ -174,7 +174,7 @@ export interface SyntaxProfile { /** Runner → host: the computed evaluation steps for the most recent run. */ export interface StepperStepsMessage { - type: "steps"; + type: 'steps'; steps: SerializedStepperStep[]; /** * The language's rendering rules. Optional and run-level (the same for every step). When absent, @@ -185,7 +185,7 @@ export interface StepperStepsMessage { /** Runner → host: stepping failed (e.g. a parse error). */ export interface StepperErrorMessage { - type: "error"; + type: 'error'; error: string; } @@ -194,7 +194,7 @@ export interface StepperErrorMessage { * display when the stepper tab is (re)opened without re-running the program. */ export interface StepperRequestMessage { - type: "request"; + type: 'request'; } /** Every message that may cross the {@link STEPPER_CHANNEL_ID} channel. */ diff --git a/src/common/stepper/tsconfig.json b/src/common/stepper/tsconfig.json index b71f4ee..c7482f9 100644 --- a/src/common/stepper/tsconfig.json +++ b/src/common/stepper/tsconfig.json @@ -5,6 +5,6 @@ "compilerOptions": { "declaration": true, "outDir": "./dist", - "rootDir": "./src", + "rootDir": "./src" } } diff --git a/src/common/tabs/rollup.config.mjs b/src/common/tabs/rollup.config.mjs index 58838a5..9a8d5e0 100644 --- a/src/common/tabs/rollup.config.mjs +++ b/src/common/tabs/rollup.config.mjs @@ -1,22 +1,21 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), typescript(), terser()], }); - diff --git a/src/common/tabs/src/index.ts b/src/common/tabs/src/index.ts index 1f1daea..923c1c9 100644 --- a/src/common/tabs/src/index.ts +++ b/src/common/tabs/src/index.ts @@ -1,5 +1,5 @@ -import type { IconName } from "@blueprintjs/icons"; -import type React from "react"; +import type { IconName } from '@blueprintjs/icons'; +import type React from 'react'; /** * The Tab interface represents a tab that can be registered with the tab service. diff --git a/src/common/test/rollup.config.mjs b/src/common/test/rollup.config.mjs index 0d387b7..9a8d5e0 100644 --- a/src/common/test/rollup.config.mjs +++ b/src/common/test/rollup.config.mjs @@ -1,20 +1,20 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), typescript(), terser()], diff --git a/src/common/test/src/__tests__/common.test.ts b/src/common/test/src/__tests__/common.test.ts index 2407bd5..5a55a9a 100644 --- a/src/common/test/src/__tests__/common.test.ts +++ b/src/common/test/src/__tests__/common.test.ts @@ -1,6 +1,6 @@ -import { CHANNEL_ID } from ".."; -import { test, expect } from "vitest"; +import { CHANNEL_ID } from '..'; +import { test, expect } from 'vitest'; -test("should have a valid channel id", () => { - expect(CHANNEL_ID).toBe("test"); +test('should have a valid channel id', () => { + expect(CHANNEL_ID).toBe('test'); }); diff --git a/src/common/test/src/index.ts b/src/common/test/src/index.ts index 8ec1998..1625ed6 100644 --- a/src/common/test/src/index.ts +++ b/src/common/test/src/index.ts @@ -1,11 +1,11 @@ -export const WEB_ID = "__web_test"; -export const RUNNER_ID = "__runner_test"; +export const WEB_ID = '__web_test'; +export const RUNNER_ID = '__runner_test'; -export const CHANNEL_ID = "test"; +export const CHANNEL_ID = 'test'; -export type TestMessage = "ping" | "pong"; +export type TestMessage = 'ping' | 'pong'; export type PySlangMessage = - | { type: "run"; code: string } - | { type: "result"; output: string } - | { type: "error"; message: string }; + | { type: 'run'; code: string } + | { type: 'result'; output: string } + | { type: 'error'; message: string }; diff --git a/src/runner/autocomplete/rollup.config.mjs b/src/runner/autocomplete/rollup.config.mjs index 244d88e..711c6d5 100644 --- a/src/runner/autocomplete/rollup.config.mjs +++ b/src/runner/autocomplete/rollup.config.mjs @@ -1,23 +1,22 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), typescript(), terser()], external: [/^@sourceacademy\/conductor(?:\/|$)/], }); - diff --git a/src/runner/autocomplete/src/__tests__/runner.test.ts b/src/runner/autocomplete/src/__tests__/runner.test.ts index df5986f..160a18a 100644 --- a/src/runner/autocomplete/src/__tests__/runner.test.ts +++ b/src/runner/autocomplete/src/__tests__/runner.test.ts @@ -1,4 +1,4 @@ -import { BaseAutoCompleteRunnerPlugin } from ".."; +import { BaseAutoCompleteRunnerPlugin } from '..'; import { AUTOCOMPLETE_CHANNEL_ID, RUNNER_PLUGIN_ID, @@ -9,20 +9,20 @@ import { type SyntaxHighlightData, type SyntaxHighlightMessage, type TransferredSyntaxHighlightData, -} from "@sourceacademy/common-autocomplete"; +} from '@sourceacademy/common-autocomplete'; import { makeRpc, type IChannel, type IConduit, type IRpcMessage, -} from "@sourceacademy/conductor/conduit"; -import { afterEach, expect, test, vi } from "vitest"; +} from '@sourceacademy/conductor/conduit'; +import { afterEach, expect, test, vi } from 'vitest'; afterEach(() => { vi.useRealTimers(); }); -test("should have a valid channel id", () => { +test('should have a valid channel id', () => { expect(BaseAutoCompleteRunnerPlugin.channelAttach).toEqual([ AUTOCOMPLETE_CHANNEL_ID, SYNTAX_CHANNEL_ID, @@ -35,19 +35,19 @@ class TestRunner extends BaseAutoCompleteRunnerPlugin { get mode(): SyntaxHighlightData { return { highlightRules: {}, - foldingRules: { hookFrom: "ace/mode/folding/cstyle", args: [] }, - lineCommentStart: "//", + foldingRules: { hookFrom: 'ace/mode/folding/cstyle', args: [] }, + lineCommentStart: '//', pairQuotesAfter: {}, indents: (value: string) => `indented:${value}`, - outdents: { hookFrom: "ace/mode/text" }, + outdents: { hookFrom: 'ace/mode/text' }, autoOutdent: (value: string) => `outdented:${value}`, - id: "rpc-test", + id: 'rpc-test', }; } autocomplete(code: string, row: number, column: number): AutoCompleteEntry[] { this.autocompleteCalls.push([code, row, column]); - return [{ name: "result", meta: "var" as AutoCompleteEntry["meta"] }]; + return [{ name: 'result', meta: 'var' as AutoCompleteEntry['meta'] }]; } } @@ -61,7 +61,7 @@ class FunctionOutdentsRunner extends TestRunner { } class TestChannel implements IChannel { - readonly name = "test"; + readonly name = 'test'; private readonly subscribers = new Set<(message: T) => void>(); peer?: TestChannel; @@ -96,55 +96,55 @@ const makeChannelPair = (): [TestChannel, TestChannel] => { return [runner, web]; }; -test("exposes the runner plugin identity", () => { +test('exposes the runner plugin identity', () => { const [runnerAutocomplete] = makeChannelPair(); const [runnerSyntax, webSyntax] = makeChannelPair(); const plugin = new TestRunner( {} as IConduit, [runnerAutocomplete, runnerSyntax] as IChannel[], ); - webSyntax.send({ type: "ack" }); + webSyntax.send({ type: 'ack' }); expect(plugin.id).toBe(RUNNER_PLUGIN_ID); }); -test("responds to autocomplete requests over its Conductor channel", () => { +test('responds to autocomplete requests over its Conductor channel', () => { const [runnerAutocomplete, webAutocomplete] = makeChannelPair(); const [runnerSyntax, webSyntax] = makeChannelPair(); const plugin = new TestRunner( {} as IConduit, [runnerAutocomplete, runnerSyntax] as IChannel[], ); - webSyntax.send({ type: "ack" }); + webSyntax.send({ type: 'ack' }); let response: AutoCompleteMessage | undefined; webAutocomplete.subscribe(message => { - if ((message as AutoCompleteMessage).type === "response") { + if ((message as AutoCompleteMessage).type === 'response') { response = message as AutoCompleteMessage; } }); webAutocomplete.send({ - type: "request", + type: 'request', requestId: 17, - code: "res", + code: 'res', row: 2, column: 3, }); webAutocomplete.send({ - type: "response", + type: 'response', requestId: 99, declarations: [], }); - expect(plugin.autocompleteCalls).toEqual([["res", 2, 3]]); + expect(plugin.autocompleteCalls).toEqual([['res', 2, 3]]); expect(response).toEqual({ - type: "response", + type: 'response', requestId: 17, - declarations: [{ name: "result", meta: "var" }], + declarations: [{ name: 'result', meta: 'var' }], }); }); -test("retries syntax transfer until the web plugin acknowledges it", () => { +test('retries syntax transfer until the web plugin acknowledges it', () => { vi.useFakeTimers(); const [runnerAutocomplete] = makeChannelPair(); const [runnerSyntax, webSyntax] = makeChannelPair(); @@ -152,7 +152,7 @@ test("retries syntax transfer until the web plugin acknowledges it", () => { const responses: SyntaxHighlightMessage[] = []; webSyntax.subscribe(message => { - if ((message as SyntaxHighlightMessage).type === "response") { + if ((message as SyntaxHighlightMessage).type === 'response') { responses.push(message as SyntaxHighlightMessage); } }); @@ -160,12 +160,12 @@ test("retries syntax transfer until the web plugin acknowledges it", () => { vi.advanceTimersByTime(3000); expect(responses).toHaveLength(3); - webSyntax.send({ type: "ack" }); + webSyntax.send({ type: 'ack' }); vi.advanceTimersByTime(3000); expect(responses).toHaveLength(3); }); -test("transfers callable mode properties as RPC references", async () => { +test('transfers callable mode properties as RPC references', async () => { const [runnerAutocomplete] = makeChannelPair(); const [runnerSyntax, webSyntax] = makeChannelPair(); @@ -177,9 +177,9 @@ test("transfers callable mode properties as RPC references", async () => { let transferredMode: TransferredSyntaxHighlightData | undefined; webSyntax.subscribe(message => { const syntaxMessage = message as SyntaxHighlightMessage; - if (syntaxMessage.type === "response") { + if (syntaxMessage.type === 'response') { transferredMode = syntaxMessage.data; - webSyntax.send({ type: "ack" }); + webSyntax.send({ type: 'ack' }); } }); const remote = makeRpc, ModeRpc>( @@ -187,23 +187,23 @@ test("transfers callable mode properties as RPC references", async () => { {}, ); - webSyntax.send({ type: "request" }); + webSyntax.send({ type: 'request' }); - expect(transferredMode?.indents).toEqual({ rpc: "indents" }); - expect(transferredMode?.outdents).toEqual({ hookFrom: "ace/mode/text" }); - expect(transferredMode?.autoOutdent).toEqual({ rpc: "autoOutdent" }); - await expect(remote.indents("line")).resolves.toBe("indented:line"); - await expect(remote.autoOutdent("line")).resolves.toBe("outdented:line"); + expect(transferredMode?.indents).toEqual({ rpc: 'indents' }); + expect(transferredMode?.outdents).toEqual({ hookFrom: 'ace/mode/text' }); + expect(transferredMode?.autoOutdent).toEqual({ rpc: 'autoOutdent' }); + await expect(remote.indents('line')).resolves.toBe('indented:line'); + await expect(remote.autoOutdent('line')).resolves.toBe('outdented:line'); expect(() => ( plugin as unknown as { __callModeFunction(name: keyof ModeRpc, args: unknown[]): unknown; } - ).__callModeFunction("outdents", ["line"]), + ).__callModeFunction('outdents', ['line']), ).toThrow('Mode function "outdents" is configured with a hook and cannot be called over RPC.'); }); -test("exposes a callable outdents mode function over RPC", async () => { +test('exposes a callable outdents mode function over RPC', async () => { const [runnerAutocomplete] = makeChannelPair(); const [runnerSyntax, webSyntax] = makeChannelPair(); new FunctionOutdentsRunner( @@ -211,8 +211,8 @@ test("exposes a callable outdents mode function over RPC", async () => { [runnerAutocomplete, runnerSyntax] as IChannel[], ); webSyntax.subscribe(message => { - if ((message as SyntaxHighlightMessage).type === "response") { - webSyntax.send({ type: "ack" }); + if ((message as SyntaxHighlightMessage).type === 'response') { + webSyntax.send({ type: 'ack' }); } }); const remote = makeRpc, ModeRpc>( @@ -220,7 +220,7 @@ test("exposes a callable outdents mode function over RPC", async () => { {}, ); - webSyntax.send({ type: "request" }); + webSyntax.send({ type: 'request' }); - await expect(remote.outdents("line")).resolves.toBe("should-outdent:line"); + await expect(remote.outdents('line')).resolves.toBe('should-outdent:line'); }); diff --git a/src/runner/autocomplete/src/index.ts b/src/runner/autocomplete/src/index.ts index 627833a..4c3086d 100644 --- a/src/runner/autocomplete/src/index.ts +++ b/src/runner/autocomplete/src/index.ts @@ -4,7 +4,7 @@ import { type IConduit, type IPlugin, type IRpcMessage, -} from "@sourceacademy/conductor/conduit"; +} from '@sourceacademy/conductor/conduit'; import { AUTOCOMPLETE_CHANNEL_ID, @@ -18,10 +18,10 @@ import { type SyntaxHighlightMessage, type TransferredModeFunction, type TransferredSyntaxHighlightData, -} from "@sourceacademy/common-autocomplete"; +} from '@sourceacademy/common-autocomplete'; const transferModeFunction = (name: keyof ModeRpc, fn: ModeFunction): TransferredModeFunction => - typeof fn === "function" ? { rpc: name } : fn; + typeof fn === 'function' ? { rpc: name } : fn; /** * This plugin provides autocomplete suggestions and syntax highlighting. @@ -63,34 +63,34 @@ export abstract class BaseAutoCompleteRunnerPlugin implements IPlugin { makeRpc>( this.__syntaxHighlightChannel as unknown as IChannel, { - indents: (...args: unknown[]) => this.__callModeFunction("indents", args), - outdents: (...args: unknown[]) => this.__callModeFunction("outdents", args), - autoOutdent: (...args: unknown[]) => this.__callModeFunction("autoOutdent", args), + indents: (...args: unknown[]) => this.__callModeFunction('indents', args), + outdents: (...args: unknown[]) => this.__callModeFunction('outdents', args), + autoOutdent: (...args: unknown[]) => this.__callModeFunction('autoOutdent', args), }, ); const handler = setInterval(() => { this.__syntaxHighlightChannel.send({ - type: "response", + type: 'response', data: this.__transferMode(), }); }, 1000); this.__syntaxHighlightChannel.subscribe((message: SyntaxHighlightMessage) => { - if (message.type === "ack") { + if (message.type === 'ack') { clearInterval(handler); - } else if (message.type === "request") { + } else if (message.type === 'request') { this.__syntaxHighlightChannel.send({ - type: "response", + type: 'response', data: this.__transferMode(), }); } }); this.__autoCompleteChannel.subscribe((message: AutoCompleteMessage) => { - if (message.type === "request") { + if (message.type === 'request') { const { requestId, code, row, column } = message; const entries = this.autocomplete(code, row, column); this.__autoCompleteChannel.send({ - type: "response", + type: 'response', requestId, declarations: entries, }); @@ -102,16 +102,16 @@ export abstract class BaseAutoCompleteRunnerPlugin implements IPlugin { const mode = this.mode; return { ...mode, - indents: transferModeFunction("indents", mode.indents), - outdents: transferModeFunction("outdents", mode.outdents), - autoOutdent: transferModeFunction("autoOutdent", mode.autoOutdent), + indents: transferModeFunction('indents', mode.indents), + outdents: transferModeFunction('outdents', mode.outdents), + autoOutdent: transferModeFunction('autoOutdent', mode.autoOutdent), }; } private __callModeFunction(name: keyof ModeRpc, args: unknown[]): unknown { const mode = this.mode; const fn = mode[name]; - if (typeof fn !== "function") { + if (typeof fn !== 'function') { throw new Error( `Mode function "${name}" is configured with a hook and cannot be called over RPC.`, ); diff --git a/src/runner/cse-machine/rollup.config.mjs b/src/runner/cse-machine/rollup.config.mjs index 0d387b7..9a8d5e0 100644 --- a/src/runner/cse-machine/rollup.config.mjs +++ b/src/runner/cse-machine/rollup.config.mjs @@ -1,20 +1,20 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), typescript(), terser()], diff --git a/src/runner/cse-machine/src/__tests__/runner.test.ts b/src/runner/cse-machine/src/__tests__/runner.test.ts index b15a96f..02c95d5 100644 --- a/src/runner/cse-machine/src/__tests__/runner.test.ts +++ b/src/runner/cse-machine/src/__tests__/runner.test.ts @@ -1,16 +1,15 @@ -import { describe, test, expect, vi } from "vitest"; -import { CseMachinePlugin } from ".."; +import { describe, test, expect, vi } from 'vitest'; +import { CseMachinePlugin } from '..'; import { CSE_CHANNEL, CSE_MESSAGE_TYPE_SNAPSHOTS, RUNNER_ID, type CseSnapshot, -} from "@sourceacademy/common-cse-machine"; -import type { IChannel, IConduit } from "@sourceacademy/conductor/conduit"; +} from '@sourceacademy/common-cse-machine'; +import type { IChannel, IConduit } from '@sourceacademy/conductor/conduit'; const makeChannel = () => { const send = vi.fn(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any return { send } as unknown as IChannel & { send: typeof send }; }; const makePlugin = (ch = makeChannel()) => new CseMachinePlugin({} as IConduit, [ch]); @@ -19,50 +18,50 @@ const minimalSnapshot = (): CseSnapshot => ({ stepIndex: 0, control: [], stash: [], - environments: [{ id: "g", name: "global", parentId: null, bindings: [], isActive: true }], + environments: [{ id: 'g', name: 'global', parentId: null, bindings: [], isActive: true }], }); // ── Identity / wiring ───────────────────────────────────────────────────────── -describe("plugin identity", () => { - test("id is RUNNER_ID", () => { +describe('plugin identity', () => { + test('id is RUNNER_ID', () => { expect(makePlugin().id).toBe(RUNNER_ID); }); - test("channelAttach declares the CSE channel", () => { + test('channelAttach declares the CSE channel', () => { expect(CseMachinePlugin.channelAttach).toEqual([CSE_CHANNEL]); }); - test("channelAttach contains exactly one channel", () => { + test('channelAttach contains exactly one channel', () => { expect(CseMachinePlugin.channelAttach).toHaveLength(1); }); }); // ── Constructor ─────────────────────────────────────────────────────────────── -describe("constructor", () => { - test("throws when no channel is provided", () => { +describe('constructor', () => { + test('throws when no channel is provided', () => { expect(() => new CseMachinePlugin({} as IConduit, [])).toThrow( - "CSE channel is required but was not provided.", + 'CSE channel is required but was not provided.', ); }); - test("does not throw when a channel is provided", () => { + test('does not throw when a channel is provided', () => { expect(() => makePlugin()).not.toThrow(); }); }); // ── sendSnapshots ───────────────────────────────────────────────────────────── -describe("sendSnapshots", () => { - test("sends a message with the correct type discriminator", () => { +describe('sendSnapshots', () => { + test('sends a message with the correct type discriminator', () => { const channel = makeChannel(); const plugin = makePlugin(channel); plugin.sendSnapshots([minimalSnapshot()]); expect(channel.send.mock.calls[0][0].type).toBe(CSE_MESSAGE_TYPE_SNAPSHOTS); }); - test("sends snapshots unchanged", () => { + test('sends snapshots unchanged', () => { const channel = makeChannel(); const plugin = makePlugin(channel); const snapshots = [minimalSnapshot()]; @@ -70,7 +69,7 @@ describe("sendSnapshots", () => { expect(channel.send.mock.calls[0][0].snapshots).toEqual(snapshots); }); - test("sets totalSteps to the length of the snapshots array", () => { + test('sets totalSteps to the length of the snapshots array', () => { const channel = makeChannel(); const plugin = makePlugin(channel); const snapshots = [minimalSnapshot(), { ...minimalSnapshot(), stepIndex: 1 }]; @@ -78,14 +77,14 @@ describe("sendSnapshots", () => { expect(channel.send.mock.calls[0][0].totalSteps).toBe(2); }); - test("calls channel.send exactly once per sendSnapshots call", () => { + test('calls channel.send exactly once per sendSnapshots call', () => { const channel = makeChannel(); const plugin = makePlugin(channel); plugin.sendSnapshots([minimalSnapshot()]); expect(channel.send).toHaveBeenCalledTimes(1); }); - test("can be called multiple times, sending once each time", () => { + test('can be called multiple times, sending once each time', () => { const channel = makeChannel(); const plugin = makePlugin(channel); plugin.sendSnapshots([minimalSnapshot()]); @@ -93,7 +92,7 @@ describe("sendSnapshots", () => { expect(channel.send).toHaveBeenCalledTimes(2); }); - test("handles an empty snapshots array", () => { + test('handles an empty snapshots array', () => { const channel = makeChannel(); const plugin = makePlugin(channel); plugin.sendSnapshots([]); @@ -105,14 +104,14 @@ describe("sendSnapshots", () => { }); }); - test("defaults breakpointSteps to an empty array when omitted", () => { + test('defaults breakpointSteps to an empty array when omitted', () => { const channel = makeChannel(); const plugin = makePlugin(channel); plugin.sendSnapshots([minimalSnapshot()]); expect(channel.send.mock.calls[0][0].breakpointSteps).toEqual([]); }); - test("forwards the breakpointSteps argument unchanged", () => { + test('forwards the breakpointSteps argument unchanged', () => { const channel = makeChannel(); const plugin = makePlugin(channel); const snapshots = [minimalSnapshot(), { ...minimalSnapshot(), stepIndex: 1 }]; @@ -120,7 +119,7 @@ describe("sendSnapshots", () => { expect(channel.send.mock.calls[0][0].breakpointSteps).toEqual([1]); }); - test("handles a large batch of snapshots", () => { + test('handles a large batch of snapshots', () => { const channel = makeChannel(); const plugin = makePlugin(channel); const snapshots = Array.from({ length: 50 }, (_, i) => ({ @@ -131,14 +130,14 @@ describe("sendSnapshots", () => { expect(channel.send.mock.calls[0][0].totalSteps).toBe(50); }); - test("preserves snapshot fields including currentLine and metadata", () => { + test('preserves snapshot fields including currentLine and metadata', () => { const channel = makeChannel(); const plugin = makePlugin(channel); const snap: CseSnapshot = { stepIndex: 3, - control: [{ displayText: "call f", metadata: { instrType: "Application" } }], - stash: [{ displayValue: "42", label: "number" }], - environments: [{ id: "g", name: "global", parentId: null, bindings: [], isActive: true }], + control: [{ displayText: 'call f', metadata: { instrType: 'Application' } }], + stash: [{ displayValue: '42', label: 'number' }], + environments: [{ id: 'g', name: 'global', parentId: null, bindings: [], isActive: true }], currentLine: 7, }; plugin.sendSnapshots([snap]); diff --git a/src/runner/cse-machine/src/index.ts b/src/runner/cse-machine/src/index.ts index fdc0902..513d948 100644 --- a/src/runner/cse-machine/src/index.ts +++ b/src/runner/cse-machine/src/index.ts @@ -4,8 +4,8 @@ import { RUNNER_ID, type CseSnapshot, type CseSnapshotMessage, -} from "@sourceacademy/common-cse-machine"; -import type { IPlugin, IChannel, IConduit } from "@sourceacademy/conductor/conduit"; +} from '@sourceacademy/common-cse-machine'; +import type { IPlugin, IChannel, IConduit } from '@sourceacademy/conductor/conduit'; /** * Runner-side CSE machine plugin. @@ -31,7 +31,7 @@ export class CseMachinePlugin implements IPlugin { [cseChannel]: IChannel[], ) { if (!cseChannel) { - throw new Error("CSE channel is required but was not provided."); + throw new Error('CSE channel is required but was not provided.'); } this.__cseChannel = cseChannel; } diff --git a/src/runner/data-visualizer/rollup.config.mjs b/src/runner/data-visualizer/rollup.config.mjs index 58838a5..9a8d5e0 100644 --- a/src/runner/data-visualizer/rollup.config.mjs +++ b/src/runner/data-visualizer/rollup.config.mjs @@ -1,22 +1,21 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), typescript(), terser()], }); - diff --git a/src/runner/data-visualizer/src/__tests__/runner.test.ts b/src/runner/data-visualizer/src/__tests__/runner.test.ts index a17e11f..abd03b8 100644 --- a/src/runner/data-visualizer/src/__tests__/runner.test.ts +++ b/src/runner/data-visualizer/src/__tests__/runner.test.ts @@ -1,11 +1,11 @@ -import { expect, test } from "vitest"; +import { expect, test } from 'vitest'; import { DATA_VISUALIZER_CHANNEL_ID, RUNNER_ID, type SerializedDataVisualizerNode, -} from "@sourceacademy/common-data-visualizer"; -import { BaseDataVisualizerRunnerPlugin, createRefIdAllocator, type RefIdAllocator } from ".."; +} from '@sourceacademy/common-data-visualizer'; +import { BaseDataVisualizerRunnerPlugin, createRefIdAllocator, type RefIdAllocator } from '..'; class FakeChannel { name = DATA_VISUALIZER_CHANNEL_ID; @@ -33,44 +33,44 @@ type TestValue = number | TestValue[]; class ArrayDataVisualizer extends BaseDataVisualizerRunnerPlugin { protected toNode(value: TestValue, refs: RefIdAllocator): SerializedDataVisualizerNode { if (!Array.isArray(value)) { - return { type: "leaf", displayValue: String(value), label: "number" }; + return { type: 'leaf', displayValue: String(value), label: 'number' }; } const { refId, alreadySeen } = refs.get(value); if (alreadySeen) { - return { type: "ref", refId }; + return { type: 'ref', refId }; } - return { type: "array", refId, children: value.map(v => this.toNode(v, refs)) }; + return { type: 'array', refId, children: value.map(v => this.toNode(v, refs)) }; } } -test("attaches to the data visualizer channel", () => { +test('attaches to the data visualizer channel', () => { expect(BaseDataVisualizerRunnerPlugin.channelAttach).toEqual([DATA_VISUALIZER_CHANNEL_ID]); }); -test("has the runner id", () => { +test('has the runner id', () => { const channel = new FakeChannel(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any + const plugin = new ArrayDataVisualizer({} as any, [channel as any]); expect(plugin.id).toBe(RUNNER_ID); }); -test("sendDrawing serializes one row and pushes the full row list", async () => { +test('sendDrawing serializes one row and pushes the full row list', async () => { const channel = new FakeChannel(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any + const plugin = new ArrayDataVisualizer({} as any, [channel as any]); await plugin.sendDrawing([1, [2, 3]]); expect(channel.sent).toEqual([ { - type: "rows", + type: 'rows', rows: [ [ - { type: "leaf", displayValue: "1", label: "number" }, + { type: 'leaf', displayValue: '1', label: 'number' }, { - type: "array", + type: 'array', refId: 1, children: [ - { type: "leaf", displayValue: "2", label: "number" }, - { type: "leaf", displayValue: "3", label: "number" }, + { type: 'leaf', displayValue: '2', label: 'number' }, + { type: 'leaf', displayValue: '3', label: 'number' }, ], }, ], @@ -79,47 +79,47 @@ test("sendDrawing serializes one row and pushes the full row list", async () => ]); }); -test("multiple sendDrawing calls accumulate rows, resending the full list each time", async () => { +test('multiple sendDrawing calls accumulate rows, resending the full list each time', async () => { const channel = new FakeChannel(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any + const plugin = new ArrayDataVisualizer({} as any, [channel as any]); await plugin.sendDrawing([1]); await plugin.sendDrawing([2]); expect(channel.sent).toHaveLength(2); expect(channel.sent[1]).toEqual({ - type: "rows", + type: 'rows', rows: [ - [{ type: "leaf", displayValue: "1", label: "number" }], - [{ type: "leaf", displayValue: "2", label: "number" }], + [{ type: 'leaf', displayValue: '1', label: 'number' }], + [{ type: 'leaf', displayValue: '2', label: 'number' }], ], }); }); -test("request replays the rows accumulated so far", async () => { +test('request replays the rows accumulated so far', async () => { const channel = new FakeChannel(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any + const plugin = new ArrayDataVisualizer({} as any, [channel as any]); await plugin.sendDrawing([1]); - channel.emit({ type: "request" }); + channel.emit({ type: 'request' }); expect(channel.sent).toHaveLength(2); expect(channel.sent[1]).toEqual(channel.sent[0]); }); -test("resetRun clears accumulated rows and pushes the empty state", async () => { +test('resetRun clears accumulated rows and pushes the empty state', async () => { const channel = new FakeChannel(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any + const plugin = new ArrayDataVisualizer({} as any, [channel as any]); await plugin.sendDrawing([1]); await plugin.resetRun(); - expect(channel.sent[1]).toEqual({ type: "rows", rows: [] }); + expect(channel.sent[1]).toEqual({ type: 'rows', rows: [] }); - channel.emit({ type: "request" }); - expect(channel.sent[2]).toEqual({ type: "rows", rows: [] }); + channel.emit({ type: 'request' }); + expect(channel.sent[2]).toEqual({ type: 'rows', rows: [] }); }); -test("a self-referential value terminates via a ref node instead of recursing forever", async () => { +test('a self-referential value terminates via a ref node instead of recursing forever', async () => { const channel = new FakeChannel(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any + const plugin = new ArrayDataVisualizer({} as any, [channel as any]); const cyclic: TestValue[] = [1]; cyclic.push(cyclic); @@ -129,16 +129,16 @@ test("a self-referential value terminates via a ref node instead of recursing fo const message = channel.sent[0] as { rows: SerializedDataVisualizerNode[][] }; const [node] = message.rows[0]; expect(node).toEqual({ - type: "array", + type: 'array', refId: 1, children: [ - { type: "leaf", displayValue: "1", label: "number" }, - { type: "ref", refId: 1 }, + { type: 'leaf', displayValue: '1', label: 'number' }, + { type: 'ref', refId: 1 }, ], }); }); -test("a value shared across two arguments of one call is fully re-serialized in each, not collapsed to an unresolvable ref", async () => { +test('a value shared across two arguments of one call is fully re-serialized in each, not collapsed to an unresolvable ref', async () => { // The host renders each argument of a draw_data(...) call as its own independent Konva Stage // (a separate ), so a "ref" node can only ever be resolved against other nodes already // built within that SAME argument's tree — there is no way to draw, or even resolve, an arrow @@ -147,7 +147,7 @@ test("a value shared across two arguments of one call is fully re-serialized in // (matching the old pre-Conductor frontend's per-argument `visitedStructures` scoping) rather than // silently collapsing the second occurrence into a ref the host can never resolve. const channel = new FakeChannel(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any + const plugin = new ArrayDataVisualizer({} as any, [channel as any]); const shared: TestValue[] = [1]; @@ -157,28 +157,28 @@ test("a value shared across two arguments of one call is fully re-serialized in const [firstArg, secondArg] = message.rows[0]; // First argument's own allocator sees `shared` first, so it claims refId 1. expect(firstArg).toEqual({ - type: "array", + type: 'array', refId: 1, - children: [{ type: "leaf", displayValue: "1", label: "number" }], + children: [{ type: 'leaf', displayValue: '1', label: 'number' }], }); // Second argument gets its own fresh allocator: its outer wrapper array (a distinct object from // `shared`) is visited first and claims refId 1 for *this* argument; `shared` is then a new value // as far as this allocator is concerned, and claims refId 2 — fully expanded, not a "ref". expect(secondArg).toEqual({ - type: "array", + type: 'array', refId: 1, children: [ { - type: "array", + type: 'array', refId: 2, - children: [{ type: "leaf", displayValue: "1", label: "number" }], + children: [{ type: 'leaf', displayValue: '1', label: 'number' }], }, - { type: "leaf", displayValue: "2", label: "number" }, + { type: 'leaf', displayValue: '2', label: 'number' }, ], }); }); -test("createRefIdAllocator assigns the same id to the same reference and different ids otherwise", () => { +test('createRefIdAllocator assigns the same id to the same reference and different ids otherwise', () => { const refs = createRefIdAllocator(); const a = {}; const b = {}; diff --git a/src/runner/data-visualizer/src/index.ts b/src/runner/data-visualizer/src/index.ts index 60e4709..760066d 100644 --- a/src/runner/data-visualizer/src/index.ts +++ b/src/runner/data-visualizer/src/index.ts @@ -5,8 +5,8 @@ import { type RefId, type SerializedDataVisualizerNode, type SerializedDataVisualizerRow, -} from "@sourceacademy/common-data-visualizer"; -import type { IChannel, IConduit, IPlugin } from "@sourceacademy/conductor/conduit"; +} from '@sourceacademy/common-data-visualizer'; +import type { IChannel, IConduit, IPlugin } from '@sourceacademy/conductor/conduit'; /** * Assigns each distinct compound runtime value (by reference) a stable {@link RefId} the first time @@ -82,8 +82,8 @@ export abstract class BaseDataVisualizerRunnerPlugin implements IPlugin this.__channel = channel; this.__channel.subscribe(message => { // The host (re)opened the data visualizer tab and wants the latest rows without a re-run. - if (message.type === "request") { - this.__channel.send({ type: "rows", rows: this.__rows }); + if (message.type === 'request') { + this.__channel.send({ type: 'rows', rows: this.__rows }); } }); } @@ -122,7 +122,7 @@ export abstract class BaseDataVisualizerRunnerPlugin implements IPlugin this.toNode(value, createRefIdAllocator()), ); this.__rows = [...this.__rows, row]; - this.__channel.send({ type: "rows", rows: this.__rows }); + this.__channel.send({ type: 'rows', rows: this.__rows }); } /** @@ -133,6 +133,6 @@ export abstract class BaseDataVisualizerRunnerPlugin implements IPlugin */ resetRun(): void { this.__rows = []; - this.__channel.send({ type: "rows", rows: this.__rows }); + this.__channel.send({ type: 'rows', rows: this.__rows }); } } diff --git a/src/runner/module-loader/rollup.config.mjs b/src/runner/module-loader/rollup.config.mjs index 0d387b7..9a8d5e0 100644 --- a/src/runner/module-loader/rollup.config.mjs +++ b/src/runner/module-loader/rollup.config.mjs @@ -1,20 +1,20 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), typescript(), terser()], diff --git a/src/runner/module-loader/src/__tests__/runner.test.ts b/src/runner/module-loader/src/__tests__/runner.test.ts index 85d27de..fd0fe6d 100644 --- a/src/runner/module-loader/src/__tests__/runner.test.ts +++ b/src/runner/module-loader/src/__tests__/runner.test.ts @@ -1,15 +1,15 @@ -import { afterEach, describe, expect, test, vi, type Mock, type MockedFunction } from "vitest"; -import { ModuleLoaderRunnerPlugin } from ".."; +import { afterEach, describe, expect, test, vi, type Mock, type MockedFunction } from 'vitest'; +import { ModuleLoaderRunnerPlugin } from '..'; import { CHANNEL_ID, ModuleLoaderMessageType, RUNNER_ID, type ModuleLoaderMessage, -} from "@sourceacademy/common-module-loader"; -import type { IChannel, IConduit } from "@sourceacademy/conductor/conduit"; -import type { IModulePlugin } from "@sourceacademy/conductor/module"; -import type { IInterfacableEvaluator, IRunnerPlugin } from "@sourceacademy/conductor/runner"; -import type { PluginClass } from "@sourceacademy/conductor/conduit"; +} from '@sourceacademy/common-module-loader'; +import type { IChannel, IConduit } from '@sourceacademy/conductor/conduit'; +import type { IModulePlugin } from '@sourceacademy/conductor/module'; +import type { IInterfacableEvaluator, IRunnerPlugin } from '@sourceacademy/conductor/runner'; +import type { PluginClass } from '@sourceacademy/conductor/conduit'; type ChannelSubscriber = (msg: ModuleLoaderMessage) => void | Promise; type TestChannel = IChannel & { @@ -26,7 +26,10 @@ const mockBundleURL2 = `data:text/javascript;charset=utf-8,${encodeURIComponent( class MockModulePlugin2 {} export default () => ({ default: MockModulePlugin2 }); `)}`; -vi.mock("module", () => ({})); + +// Used for the mock module that the plugin needs to try import +// eslint-disable-next-line vitest/prefer-import-in-mock +vi.mock('module', () => ({})); const makeChannel = ( getResponse?: ( @@ -105,24 +108,24 @@ afterEach(() => { ModuleLoaderRunnerPlugin.instance = null; }); -describe("plugin identity", () => { - test("id is RUNNER_ID", () => { +describe('plugin identity', () => { + test('id is RUNNER_ID', () => { expect(makePlugin().plugin.id).toBe(RUNNER_ID); }); - test("channelAttach declares the module loader channel", () => { + test('channelAttach declares the module loader channel', () => { expect(ModuleLoaderRunnerPlugin.channelAttach).toEqual([CHANNEL_ID]); }); - test("constructor sets the singleton instance", () => { + test('constructor sets the singleton instance', () => { const { plugin } = makePlugin(); expect(ModuleLoaderRunnerPlugin.instance).toBe(plugin); }); }); -describe("requestModule", () => { - test("requests a module, imports the returned bundle, registers it, and initialises it", async () => { - const tabs = ["ChartTab", "SettingsTab"]; +describe('requestModule', () => { + test('requests a module, imports the returned bundle, registers it, and initialises it', async () => { + const tabs = ['ChartTab', 'SettingsTab']; const channel = makeChannel(msg => { if (msg.type !== ModuleLoaderMessageType.REQUEST_MODULE) { return undefined; @@ -136,24 +139,24 @@ describe("requestModule", () => { }); const { plugin, evaluator, registerPlugin, initialise, pluginObj } = makePlugin(channel); - await expect(plugin.requestModule("chart")).resolves.toBe(pluginObj); + await expect(plugin.requestModule('chart')).resolves.toBe(pluginObj); expect(channel.subscribe).toHaveBeenCalledOnce(); expect(channel.send).toHaveBeenCalledWith({ type: ModuleLoaderMessageType.REQUEST_MODULE, - moduleName: "chart", + moduleName: 'chart', }); expect(channel.unsubscribe).toHaveBeenCalledOnce(); expect(registerPlugin).toHaveBeenCalledOnce(); const [pluginClass, receivedEvaluator, options] = registerPlugin.mock.calls[0]; expect(pluginClass).toEqual(expect.any(Function)); - expect(pluginClass.name).toBe("MockModulePlugin"); + expect(pluginClass.name).toBe('MockModulePlugin'); expect(receivedEvaluator).toBe(evaluator); expect(options).toMatchObject({ tabs }); expect(initialise).toHaveBeenCalledOnce(); }); - test("loadTab delegates to the conductor for declared tabs only", async () => { + test('loadTab delegates to the conductor for declared tabs only', async () => { const channel = makeChannel(msg => { if (msg.type !== ModuleLoaderMessageType.REQUEST_MODULE) { return undefined; @@ -162,12 +165,12 @@ describe("requestModule", () => { type: ModuleLoaderMessageType.MODULE_RESPONSE, moduleName: msg.moduleName, moduleURL: mockBundleURL, - tabs: ["ChartTab"], + tabs: ['ChartTab'], }; }); const { plugin, registerPlugin, hostLoadPlugin } = makePlugin(channel); - await plugin.requestModule("chart"); + await plugin.requestModule('chart'); const options = registerPlugin.mock.calls[0][2]; let resolveHostLoadPlugin!: () => void; @@ -177,20 +180,20 @@ describe("requestModule", () => { }), ); - const loadTabPromise = options.loadTab("ChartTab"); - expect(hostLoadPlugin).toHaveBeenCalledWith("ChartTab"); + const loadTabPromise = options.loadTab('ChartTab'); + expect(hostLoadPlugin).toHaveBeenCalledWith('ChartTab'); await expect( - Promise.race([loadTabPromise.then(() => "resolved"), Promise.resolve("pending")]), - ).resolves.toBe("pending"); + Promise.race([loadTabPromise.then(() => 'resolved'), Promise.resolve('pending')]), + ).resolves.toBe('pending'); resolveHostLoadPlugin(); await expect(loadTabPromise).resolves.toBeUndefined(); - await expect(() => options.loadTab("MissingTab")).rejects.toThrow( - "Tab MissingTab not found in module chart", + await expect(() => options.loadTab('MissingTab')).rejects.toThrow( + 'Tab MissingTab not found in module chart', ); }); - test("rejects when the plugin is invalid", () => { + test('rejects when the plugin is invalid', async () => { // mock the import function const channel = makeChannel(msg => { if (msg.type !== ModuleLoaderMessageType.REQUEST_MODULE) { @@ -199,17 +202,21 @@ describe("requestModule", () => { return { type: ModuleLoaderMessageType.MODULE_RESPONSE, moduleName: msg.moduleName, - moduleURL: "module", - tabs: ["Tab1"], + moduleURL: 'module', + tabs: ['Tab1'], }; }); const { plugin } = makePlugin(channel); - plugin.requestModule("module").catch(err => { + + try { + await plugin.requestModule('module'); + expect.fail('Error should have been thrown'); + } catch (err) { expect(err).toBeInstanceOf(Error); - }); + } }); - test("out of order responses are ignored", async () => { + test('out of order responses are ignored', async () => { const channel = makeChannel(msg => { if (msg.type !== ModuleLoaderMessageType.REQUEST_MODULE) { return undefined; @@ -217,33 +224,33 @@ describe("requestModule", () => { return [ { type: ModuleLoaderMessageType.MODULE_RESPONSE, - moduleName: "module2", + moduleName: 'module2', moduleURL: mockBundleURL2, - tabs: ["Tab2"], + tabs: ['Tab2'], }, { type: ModuleLoaderMessageType.MODULE_RESPONSE, - moduleName: "module", + moduleName: 'module', moduleURL: mockBundleURL, - tabs: ["Tab1"], + tabs: ['Tab1'], }, ]; }); const { plugin, pluginObj, registerPlugin } = makePlugin(channel); - await expect(plugin.requestModule("module")).resolves.toBe(pluginObj); + await expect(plugin.requestModule('module')).resolves.toBe(pluginObj); expect(channel.subscribe).toHaveBeenCalledOnce(); expect(channel.send).toHaveBeenCalledWith({ type: ModuleLoaderMessageType.REQUEST_MODULE, - moduleName: "module", + moduleName: 'module', }); expect(channel.unsubscribe).toHaveBeenCalledOnce(); expect(registerPlugin).toHaveBeenCalledOnce(); const [pluginClass] = registerPlugin.mock.calls[0]; - expect(pluginClass.name).toBe("MockModulePlugin"); + expect(pluginClass.name).toBe('MockModulePlugin'); }); - test("module requests are cached", async () => { + test('module requests are cached', async () => { const channel = makeChannel(msg => { if (msg.type !== ModuleLoaderMessageType.REQUEST_MODULE) { return undefined; @@ -252,15 +259,15 @@ describe("requestModule", () => { type: ModuleLoaderMessageType.MODULE_RESPONSE, moduleName: msg.moduleName, moduleURL: mockBundleURL, - tabs: ["Tab1"], + tabs: ['Tab1'], }; }); const { plugin, pluginObj, registerPlugin } = makePlugin(channel); - await plugin.requestModule("chart"); - await expect(plugin.requestModule("chart")).resolves.toBe(pluginObj); + await plugin.requestModule('chart'); + await expect(plugin.requestModule('chart')).resolves.toBe(pluginObj); }); - test("rejects when the mocked web side returns a module error", async () => { + test('rejects when the mocked web side returns a module error', async () => { const channel = makeChannel(msg => { if (msg.type !== ModuleLoaderMessageType.REQUEST_MODULE) { return undefined; @@ -268,12 +275,12 @@ describe("requestModule", () => { return { type: ModuleLoaderMessageType.MODULE_ERROR, moduleName: msg.moduleName, - error: "Module not found: missing", + error: 'Module not found: missing', }; }); const { plugin, registerPlugin } = makePlugin(channel); - await expect(plugin.requestModule("missing")).rejects.toThrow("Module not found: missing"); + await expect(plugin.requestModule('missing')).rejects.toThrow('Module not found: missing'); expect(channel.unsubscribe).toHaveBeenCalledOnce(); expect(registerPlugin).not.toHaveBeenCalled(); }); diff --git a/src/runner/module-loader/src/index.ts b/src/runner/module-loader/src/index.ts index 4f618db..d3e1c94 100644 --- a/src/runner/module-loader/src/index.ts +++ b/src/runner/module-loader/src/index.ts @@ -3,15 +3,15 @@ import { ModuleLoaderMessageType, RUNNER_ID, type ModuleLoaderMessage, -} from "@sourceacademy/common-module-loader"; +} from '@sourceacademy/common-module-loader'; import { checkIsPluginClass, type IChannel, type IConduit, type IPlugin, -} from "@sourceacademy/conductor/conduit"; -import type { IModulePlugin } from "@sourceacademy/conductor/module"; -import type { IInterfacableEvaluator, IRunnerPlugin } from "@sourceacademy/conductor/runner"; +} from '@sourceacademy/conductor/conduit'; +import type { IModulePlugin } from '@sourceacademy/conductor/module'; +import type { IInterfacableEvaluator, IRunnerPlugin } from '@sourceacademy/conductor/runner'; export class ModuleLoaderRunnerPlugin implements IPlugin { readonly id: string = RUNNER_ID; diff --git a/src/runner/remoteExecution/index.ts b/src/runner/remoteExecution/index.ts index b57bc7e..011eeb5 100644 --- a/src/runner/remoteExecution/index.ts +++ b/src/runner/remoteExecution/index.ts @@ -1,6 +1,6 @@ -import { CHANNEL_ID, RUNNER_ID, type PySlangMessage } from "@sourceacademy/common-test"; -import type { IPlugin, IChannel, IConduit } from "@sourceacademy/conductor/conduit"; -import { EV3Engine } from "py-slang/src/engines/ev3/EV3Engine"; +import { CHANNEL_ID, RUNNER_ID, type PySlangMessage } from '@sourceacademy/common-test'; +import type { IPlugin, IChannel, IConduit } from '@sourceacademy/conductor/conduit'; +import { EV3Engine } from 'py-slang/src/engines/ev3/EV3Engine'; export class remoteRunnerPlugin implements IPlugin { readonly id: string = RUNNER_ID; @@ -14,10 +14,10 @@ export class remoteRunnerPlugin implements IPlugin { this.engine = new EV3Engine(); this.__channel.subscribe(async message => { - if (message.type === "run") { + if (message.type === 'run') { const result = await this.engine.execute(message.code); - console.log("Engine response:", result); - this.__channel.send({ type: "result", output: JSON.stringify(result) }); + console.log('Engine response:', result); + this.__channel.send({ type: 'result', output: JSON.stringify(result) }); } }); } diff --git a/src/runner/remoteExecution/rollup.config.mjs b/src/runner/remoteExecution/rollup.config.mjs index 0284287..5770caf 100644 --- a/src/runner/remoteExecution/rollup.config.mjs +++ b/src/runner/remoteExecution/rollup.config.mjs @@ -1,21 +1,21 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; export default defineConfig({ - input: "src/index.ts", - external: id => id.includes("py-slang"), + input: 'src/index.ts', + external: id => id.includes('py-slang'), output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), typescript(), terser()], diff --git a/src/runner/remoteExecution/src/index.ts b/src/runner/remoteExecution/src/index.ts index e337998..27de399 100644 --- a/src/runner/remoteExecution/src/index.ts +++ b/src/runner/remoteExecution/src/index.ts @@ -1 +1 @@ -export { remoteRunnerPlugin } from "../index"; +export { remoteRunnerPlugin } from '../index'; diff --git a/src/runner/stepper/rollup.config.mjs b/src/runner/stepper/rollup.config.mjs index 58838a5..9a8d5e0 100644 --- a/src/runner/stepper/rollup.config.mjs +++ b/src/runner/stepper/rollup.config.mjs @@ -1,22 +1,21 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), typescript(), terser()], }); - diff --git a/src/runner/stepper/src/__tests__/runner.test.ts b/src/runner/stepper/src/__tests__/runner.test.ts index 9c91db8..a5a2171 100644 --- a/src/runner/stepper/src/__tests__/runner.test.ts +++ b/src/runner/stepper/src/__tests__/runner.test.ts @@ -1,7 +1,7 @@ -import { expect, test } from "vitest"; +import { expect, test } from 'vitest'; -import { RUNNER_ID, STEPPER_CHANNEL_ID } from "@sourceacademy/common-stepper"; -import { BaseStepperRunnerPlugin } from ".."; +import { RUNNER_ID, STEPPER_CHANNEL_ID } from '@sourceacademy/common-stepper'; +import { BaseStepperRunnerPlugin } from '..'; class FakeChannel { name = STEPPER_CHANNEL_ID; @@ -26,50 +26,47 @@ class FakeChannel { class CountingStepper extends BaseStepperRunnerPlugin { getSteps(ast: number) { return Array.from({ length: ast }, (_, i) => ({ - ast: { type: "Literal", nodeId: String(i), value: i }, + ast: { type: 'Literal', nodeId: String(i), value: i }, })); } } -test("attaches to the stepper channel", () => { +test('attaches to the stepper channel', () => { expect(BaseStepperRunnerPlugin.channelAttach).toEqual([STEPPER_CHANNEL_ID]); }); -test("has the runner id", () => { +test('has the runner id', () => { const channel = new FakeChannel(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any const plugin = new CountingStepper({} as any, [channel as any]); expect(plugin.id).toBe(RUNNER_ID); }); -test("sendSteps computes, caches and pushes steps; request replays them", async () => { +test('sendSteps computes, caches and pushes steps; request replays them', async () => { const channel = new FakeChannel(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any const plugin = new CountingStepper({} as any, [channel as any]); await plugin.sendSteps(2); expect(channel.sent).toEqual([ { - type: "steps", + type: 'steps', steps: [ - { ast: { type: "Literal", nodeId: "0", value: 0 } }, - { ast: { type: "Literal", nodeId: "1", value: 1 } }, + { ast: { type: 'Literal', nodeId: '0', value: 0 } }, + { ast: { type: 'Literal', nodeId: '1', value: 1 } }, ], }, ]); - channel.emit({ type: "request" }); + channel.emit({ type: 'request' }); expect(channel.sent).toHaveLength(2); expect(channel.sent[1]).toEqual(channel.sent[0]); }); -test("sendSteps reports errors instead of throwing", async () => { +test('sendSteps reports errors instead of throwing', async () => { const channel = new FakeChannel(); class Boom extends BaseStepperRunnerPlugin { getSteps(): never { - throw new Error("kaboom"); + throw new Error('kaboom'); } } - // eslint-disable-next-line @typescript-eslint/no-explicit-any const plugin = new Boom({} as any, [channel as any]); await plugin.sendSteps(1); - expect(channel.sent).toEqual([{ type: "error", error: "kaboom" }]); + expect(channel.sent).toEqual([{ type: 'error', error: 'kaboom' }]); }); diff --git a/src/runner/stepper/src/index.ts b/src/runner/stepper/src/index.ts index 50ada26..2d8115f 100644 --- a/src/runner/stepper/src/index.ts +++ b/src/runner/stepper/src/index.ts @@ -4,8 +4,8 @@ import { type SerializedStepperStep, type StepperMessage, type SyntaxProfile, -} from "@sourceacademy/common-stepper"; -import type { IChannel, IConduit, IPlugin } from "@sourceacademy/conductor/conduit"; +} from '@sourceacademy/common-stepper'; +import type { IChannel, IConduit, IPlugin } from '@sourceacademy/conductor/conduit'; /** * The language-agnostic runner half of the stepper. @@ -35,9 +35,9 @@ export abstract class BaseStepperRunnerPlugin implements IPlugin this.__stepperChannel = stepperChannel; this.__stepperChannel.subscribe(message => { // The host re-opened the stepper tab and wants the latest steps without a re-run. - if (message.type === "request") { + if (message.type === 'request') { this.__stepperChannel.send({ - type: "steps", + type: 'steps', steps: this.__lastSteps, profile: this.getSyntaxProfile(), }); @@ -76,10 +76,10 @@ export abstract class BaseStepperRunnerPlugin implements IPlugin try { const steps = await this.getSteps(ast); this.__lastSteps = steps; - this.__stepperChannel.send({ type: "steps", steps, profile: this.getSyntaxProfile() }); + this.__stepperChannel.send({ type: 'steps', steps, profile: this.getSyntaxProfile() }); } catch (error) { this.__stepperChannel.send({ - type: "error", + type: 'error', error: error instanceof Error ? error.message : String(error), }); } diff --git a/src/runner/stepper/tsconfig.json b/src/runner/stepper/tsconfig.json index b71f4ee..c7482f9 100644 --- a/src/runner/stepper/tsconfig.json +++ b/src/runner/stepper/tsconfig.json @@ -5,6 +5,6 @@ "compilerOptions": { "declaration": true, "outDir": "./dist", - "rootDir": "./src", + "rootDir": "./src" } } diff --git a/src/runner/test/rollup.config.mjs b/src/runner/test/rollup.config.mjs index 0d387b7..9a8d5e0 100644 --- a/src/runner/test/rollup.config.mjs +++ b/src/runner/test/rollup.config.mjs @@ -1,20 +1,20 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), typescript(), terser()], diff --git a/src/runner/test/src/__tests__/runner.test.ts b/src/runner/test/src/__tests__/runner.test.ts index 9fd886c..1978e45 100644 --- a/src/runner/test/src/__tests__/runner.test.ts +++ b/src/runner/test/src/__tests__/runner.test.ts @@ -1,22 +1,26 @@ -import { vi, test, expect } from "vitest"; +import { vi, test, expect } from 'vitest'; -vi.mock("py-slang/src/engines/ev3/EV3Engine", () => ({ - EV3Engine: class MockEV3Engine { - execute(code: string) { - return Promise.resolve({ output: `mock: ${code}` }); - } - }, -})); +vi.mock( + import('py-slang/src/engines/ev3/EV3Engine'), + () => + ({ + EV3Engine: class MockEV3Engine { + execute(code: string) { + return Promise.resolve({ output: `mock: ${code}` }); + } + }, + }) as any, +); -import { remoteRunnerPlugin } from "@sourceacademy/runner-remote-execution"; -import type { PySlangMessage } from "@sourceacademy/common-test"; +import { remoteRunnerPlugin } from '@sourceacademy/runner-remote-execution'; +import type { PySlangMessage } from '@sourceacademy/common-test'; -test("plugin subscribes to channel and sends result on run message", async () => { +test('plugin subscribes to channel and sends result on run message', async () => { const sentMessages: PySlangMessage[] = []; let messageHandler: (msg: PySlangMessage) => void = () => {}; const mockChannel = { - name: "py_slang_channel", + name: 'py_slang_channel', send: (msg: PySlangMessage) => { sentMessages.push(msg); }, @@ -27,13 +31,11 @@ test("plugin subscribes to channel and sends result on run message", async () => close: () => {}, }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any const mockConduit = {} as any; - // eslint-disable-next-line @typescript-eslint/no-explicit-any new remoteRunnerPlugin(mockConduit, [mockChannel as any]); - await messageHandler({ type: "run", code: "1 + 1" }); + await messageHandler({ type: 'run', code: '1 + 1' }); expect(sentMessages.length).toBeGreaterThan(0); - expect(sentMessages[0]).toMatchObject({ type: "result" }); + expect(sentMessages[0]).toMatchObject({ type: 'result' }); }); diff --git a/src/runner/test/src/index.ts b/src/runner/test/src/index.ts index 2fd8b2c..71a5f15 100644 --- a/src/runner/test/src/index.ts +++ b/src/runner/test/src/index.ts @@ -1,10 +1,10 @@ -import { CHANNEL_ID, RUNNER_ID, type TestMessage } from "@sourceacademy/common-test"; +import { CHANNEL_ID, RUNNER_ID, type TestMessage } from '@sourceacademy/common-test'; import { checkIsPluginClass, type IChannel, type IConduit, type IPlugin, -} from "@sourceacademy/conductor/conduit"; +} from '@sourceacademy/conductor/conduit'; export abstract class TestPlugin implements IPlugin { readonly id: string = RUNNER_ID; @@ -19,7 +19,7 @@ export abstract class TestPlugin implements IPlugin { this.__testChannel.subscribe(message => { console.log(message); }); - this.__testChannel.send("ping"); + this.__testChannel.send('ping'); } } checkIsPluginClass(TestPlugin); diff --git a/src/web/autocomplete/rollup.config.mjs b/src/web/autocomplete/rollup.config.mjs index 6e487db..7aadcd2 100644 --- a/src/web/autocomplete/rollup.config.mjs +++ b/src/web/autocomplete/rollup.config.mjs @@ -1,23 +1,23 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; -import commonjs from "@rollup/plugin-commonjs"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; +import commonjs from '@rollup/plugin-commonjs'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), commonjs(), typescript(), terser()], - external: ["react", "react-dom", "react/jsx-runtime", /^@sourceacademy\/conductor(?:\/|$)/], + external: ['react', 'react-dom', 'react/jsx-runtime', /^@sourceacademy\/conductor(?:\/|$)/], }); diff --git a/src/web/autocomplete/src/__tests__/web.test.ts b/src/web/autocomplete/src/__tests__/web.test.ts index 9413641..4050b06 100644 --- a/src/web/autocomplete/src/__tests__/web.test.ts +++ b/src/web/autocomplete/src/__tests__/web.test.ts @@ -6,19 +6,19 @@ import { type ModeRpc, type SyntaxHighlightData, type SyntaxHighlightMessage, -} from "@sourceacademy/common-autocomplete"; +} from '@sourceacademy/common-autocomplete'; import { makeRpc, type IChannel, type IConduit, type IRpcMessage, -} from "@sourceacademy/conductor/conduit"; -import { expect, test, vi } from "vitest"; +} from '@sourceacademy/conductor/conduit'; +import { expect, test, vi } from 'vitest'; -import { BaseAutoCompleteWebPlugin } from ".."; +import { BaseAutoCompleteWebPlugin } from '..'; class TestChannel implements IChannel { - readonly name = "test"; + readonly name = 'test'; private readonly subscribers = new Set<(message: T) => void>(); peer?: TestChannel; @@ -56,16 +56,16 @@ const makeChannelPair = (): [TestChannel, TestChannel] => { const makeSyntaxData = (id: string) => ({ highlightRules: {}, - foldingRules: { hookFrom: "ace/mode/folding/cstyle", args: [] }, - lineCommentStart: "//", + foldingRules: { hookFrom: 'ace/mode/folding/cstyle', args: [] }, + lineCommentStart: '//', pairQuotesAfter: {}, - indents: { hookFrom: "ace/mode/text" }, - outdents: { hookFrom: "ace/mode/text" }, - autoOutdent: { hookFrom: "ace/mode/text" }, + indents: { hookFrom: 'ace/mode/text' }, + outdents: { hookFrom: 'ace/mode/text' }, + autoOutdent: { hookFrom: 'ace/mode/text' }, id, }) as const; -test("exposes its identity and defers mode loading until after subclass initialization", () => { +test('exposes its identity and defers mode loading until after subclass initialization', () => { const [, webAutocomplete] = makeChannelPair(); const [runnerSyntax, webSyntax] = makeChannelPair(); @@ -80,41 +80,41 @@ test("exposes its identity and defers mode loading until after subclass initiali loadMode(data: SyntaxHighlightData): void { if (!this.initialized) { - throw new Error("loadMode called before subclass initialization"); + throw new Error('loadMode called before subclass initialization'); } this.loadedModeId = data.id; } } const plugin = new TestWeb({} as IConduit, [webAutocomplete, webSyntax] as IChannel[]); - runnerSyntax.send({ type: "response", data: makeSyntaxData("identity-test") }); + runnerSyntax.send({ type: 'response', data: makeSyntaxData('identity-test') }); expect(plugin.id).toBe(WEB_PLUGIN_ID); - expect(plugin.loadedModeId).toBe("identity-test"); + expect(plugin.loadedModeId).toBe('identity-test'); expect(BaseAutoCompleteWebPlugin.channelAttach).toEqual([ AUTOCOMPLETE_CHANNEL_ID, SYNTAX_CHANNEL_ID, ]); }); -test("forwards autocomplete requests and unsubscribes after the matching response", () => { +test('forwards autocomplete requests and unsubscribes after the matching response', () => { const [runnerAutocomplete, webAutocomplete] = makeChannelPair(); const [, webSyntax] = makeChannelPair(); - const requests: Extract[] = []; + const requests: Extract[] = []; runnerAutocomplete.subscribe(message => { const autocompleteMessage = message as AutoCompleteMessage; - if (autocompleteMessage.type === "request") { + if (autocompleteMessage.type === 'request') { requests.push(autocompleteMessage); runnerAutocomplete.send({ - type: "request", + type: 'request', requestId: -1, - code: "ignored", + code: 'ignored', row: 1, column: 1, }); runnerAutocomplete.send({ - type: "response", + type: 'response', requestId: autocompleteMessage.requestId, - declarations: [{ name: "answer", meta: "var" }], + declarations: [{ name: 'answer', meta: 'var' }], }); } }); @@ -125,37 +125,36 @@ test("forwards autocomplete requests and unsubscribes after the matching respons const plugin = new TestWeb({} as IConduit, [webAutocomplete, webSyntax] as IChannel[]); const callback = vi.fn(); - plugin.autocomplete("ans", 4, 2, callback); + plugin.autocomplete('ans', 4, 2, callback); runnerAutocomplete.send({ - type: "response", + type: 'response', requestId: requests[0].requestId, - declarations: [{ name: "ignored-second-response", meta: "var" }], + declarations: [{ name: 'ignored-second-response', meta: 'var' }], }); expect(requests).toEqual([ { - type: "request", + type: 'request', requestId: expect.any(Number), - code: "ans", + code: 'ans', row: 4, column: 2, }, ]); - expect(callback).toHaveBeenCalledOnce(); - expect(callback).toHaveBeenCalledWith({ - type: "response", + expect(callback).toHaveBeenCalledExactlyOnceWith({ + type: 'response', requestId: requests[0].requestId, - declarations: [{ name: "answer", meta: "var" }], + declarations: [{ name: 'answer', meta: 'var' }], }); }); -test("keeps overlapping autocomplete responses correlated with their requests", () => { +test('keeps overlapping autocomplete responses correlated with their requests', () => { const [runnerAutocomplete, webAutocomplete] = makeChannelPair(); const [, webSyntax] = makeChannelPair(); - const requests: Extract[] = []; + const requests: Extract[] = []; runnerAutocomplete.subscribe(message => { const autocompleteMessage = message as AutoCompleteMessage; - if (autocompleteMessage.type === "request") { + if (autocompleteMessage.type === 'request') { requests.push(autocompleteMessage); } }); @@ -167,41 +166,39 @@ test("keeps overlapping autocomplete responses correlated with their requests", const firstCallback = vi.fn(); const secondCallback = vi.fn(); - plugin.autocomplete("first", 1, 5, firstCallback); - plugin.autocomplete("second", 2, 6, secondCallback); + plugin.autocomplete('first', 1, 5, firstCallback); + plugin.autocomplete('second', 2, 6, secondCallback); expect(requests).toHaveLength(2); runnerAutocomplete.send({ - type: "response", + type: 'response', requestId: Math.max(requests[0].requestId, requests[1].requestId) + 1000, - declarations: [{ name: "unmatched", meta: "var" }], + declarations: [{ name: 'unmatched', meta: 'var' }], }); runnerAutocomplete.send({ - type: "response", + type: 'response', requestId: requests[1].requestId, - declarations: [{ name: "second-result", meta: "var" }], + declarations: [{ name: 'second-result', meta: 'var' }], }); runnerAutocomplete.send({ - type: "response", + type: 'response', requestId: requests[0].requestId, - declarations: [{ name: "first-result", meta: "var" }], + declarations: [{ name: 'first-result', meta: 'var' }], }); - expect(firstCallback).toHaveBeenCalledOnce(); - expect(firstCallback).toHaveBeenCalledWith({ - type: "response", + expect(firstCallback).toHaveBeenCalledExactlyOnceWith({ + type: 'response', requestId: requests[0].requestId, - declarations: [{ name: "first-result", meta: "var" }], + declarations: [{ name: 'first-result', meta: 'var' }], }); - expect(secondCallback).toHaveBeenCalledOnce(); - expect(secondCallback).toHaveBeenCalledWith({ - type: "response", + expect(secondCallback).toHaveBeenCalledExactlyOnceWith({ + type: 'response', requestId: requests[1].requestId, - declarations: [{ name: "second-result", meta: "var" }], + declarations: [{ name: 'second-result', meta: 'var' }], }); }); -test("hydrates mode functions as RPC calls and preserves hooks", async () => { +test('hydrates mode functions as RPC calls and preserves hooks', async () => { const [, webAutocomplete] = makeChannelPair(); const [runnerSyntax, webSyntax] = makeChannelPair(); @@ -220,41 +217,35 @@ test("hydrates mode functions as RPC calls and preserves hooks", async () => { new TestWeb({} as IConduit, [webAutocomplete, webSyntax] as IChannel[]); runnerSyntax.send({ - type: "response", + type: 'response', data: { - ...makeSyntaxData("rpc-test"), - indents: { rpc: "indents" }, - autoOutdent: { rpc: "autoOutdent" }, + ...makeSyntaxData('rpc-test'), + indents: { rpc: 'indents' }, + autoOutdent: { rpc: 'autoOutdent' }, }, }); expect(loadedMode).toBeDefined(); - expect(loadedMode?.outdents).toEqual({ hookFrom: "ace/mode/text" }); + expect(loadedMode?.outdents).toEqual({ hookFrom: 'ace/mode/text' }); const indent = loadedMode?.indents; - expect(typeof indent).toBe("function"); - if (typeof indent === "function") { - await expect((indent as (...args: unknown[]) => unknown)("line")).resolves.toBe( - "indented:line", - ); - } + expect(typeof indent).toBe('function'); + await expect((indent as (...args: unknown[]) => unknown)('line')).resolves.toBe('indented:line'); const autoOutdent = loadedMode?.autoOutdent; - expect(typeof autoOutdent).toBe("function"); - if (typeof autoOutdent === "function") { - await expect((autoOutdent as (...args: unknown[]) => unknown)("line")).resolves.toBe( - "outdented:line", - ); - } + expect(typeof autoOutdent).toBe('function'); + await expect((autoOutdent as (...args: unknown[]) => unknown)('line')).resolves.toBe( + 'outdented:line', + ); }); -test("acknowledges and loads only the first syntax response", () => { +test('acknowledges and loads only the first syntax response', () => { const [, webAutocomplete] = makeChannelPair(); const [runnerSyntax, webSyntax] = makeChannelPair(); const acknowledgements: SyntaxHighlightMessage[] = []; runnerSyntax.subscribe(message => { const syntaxMessage = message as SyntaxHighlightMessage; - if (syntaxMessage.type === "ack") { + if (syntaxMessage.type === 'ack') { acknowledgements.push(syntaxMessage); } }); @@ -267,13 +258,12 @@ test("acknowledges and loads only the first syntax response", () => { } new TestWeb({} as IConduit, [webAutocomplete, webSyntax] as IChannel[]); - const data = makeSyntaxData("first-mode"); - runnerSyntax.send({ type: "request" }); - runnerSyntax.send({ type: "ack" }); - runnerSyntax.send({ type: "response", data }); - runnerSyntax.send({ type: "response", data: { ...data, id: "second-mode" } }); + const data = makeSyntaxData('first-mode'); + runnerSyntax.send({ type: 'request' }); + runnerSyntax.send({ type: 'ack' }); + runnerSyntax.send({ type: 'response', data }); + runnerSyntax.send({ type: 'response', data: { ...data, id: 'second-mode' } }); - expect(acknowledgements).toEqual([{ type: "ack" }]); - expect(loadMode).toHaveBeenCalledOnce(); - expect(loadMode).toHaveBeenCalledWith(expect.objectContaining({ id: "first-mode" })); + expect(acknowledgements).toEqual([{ type: 'ack' }]); + expect(loadMode).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({ id: 'first-mode' })); }); diff --git a/src/web/autocomplete/src/index.ts b/src/web/autocomplete/src/index.ts index 537ac04..6771070 100644 --- a/src/web/autocomplete/src/index.ts +++ b/src/web/autocomplete/src/index.ts @@ -5,7 +5,7 @@ import { type IPlugin, type IRpcMessage, type Remote, -} from "@sourceacademy/conductor/conduit"; +} from '@sourceacademy/conductor/conduit'; import { AUTOCOMPLETE_CHANNEL_ID, SYNTAX_CHANNEL_ID, @@ -18,7 +18,7 @@ import { type SyntaxHighlightMessage, type TransferredModeFunction, type TransferredSyntaxHighlightData, -} from "@sourceacademy/common-autocomplete"; +} from '@sourceacademy/common-autocomplete'; let nextAutoCompleteRequestId = 0; @@ -45,14 +45,14 @@ export abstract class BaseAutoCompleteWebPlugin implements IPlugin { ) { const requestId = nextAutoCompleteRequestId++; const handler = (message: AutoCompleteMessage) => { - if (message.type === "response" && message.requestId === requestId) { + if (message.type === 'response' && message.requestId === requestId) { this.__autoCompleteChannel.unsubscribe(handler); callback(message); } }; this.__autoCompleteChannel.subscribe(handler); this.__autoCompleteChannel.send({ - type: "request", + type: 'request', requestId, code, row, @@ -76,14 +76,14 @@ export abstract class BaseAutoCompleteWebPlugin implements IPlugin { {}, ); const handler = (message: SyntaxHighlightMessage) => { - if (message.type === "response") { - this.__syntaxChannel.send({ type: "ack" }); + if (message.type === 'response') { + this.__syntaxChannel.send({ type: 'ack' }); this.__syntaxChannel.unsubscribe(handler); this.loadMode(this.__hydrateMode(message.data)); } }; this.__syntaxChannel.subscribe(handler); - this.__syntaxChannel.send({ type: "request" }); + this.__syntaxChannel.send({ type: 'request' }); } private __hydrateMode(data: TransferredSyntaxHighlightData): SyntaxHighlightData { @@ -96,7 +96,7 @@ export abstract class BaseAutoCompleteWebPlugin implements IPlugin { } private __hydrateModeFunction(fn: TransferredModeFunction): ModeFunction { - if ("rpc" in fn) { + if ('rpc' in fn) { const remote = this.__modeRpc[fn.rpc]; return (...args: unknown[]) => remote(...args); } diff --git a/src/web/cse-machine/rollup.config.mjs b/src/web/cse-machine/rollup.config.mjs index 0d387b7..9a8d5e0 100644 --- a/src/web/cse-machine/rollup.config.mjs +++ b/src/web/cse-machine/rollup.config.mjs @@ -1,20 +1,20 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), typescript(), terser()], diff --git a/src/web/cse-machine/src/__tests__/web.test.ts b/src/web/cse-machine/src/__tests__/web.test.ts index 0926db1..71c7dba 100644 --- a/src/web/cse-machine/src/__tests__/web.test.ts +++ b/src/web/cse-machine/src/__tests__/web.test.ts @@ -1,12 +1,12 @@ -import { describe, test, expect, vi } from "vitest"; -import { CseMachineHostPlugin } from ".."; +import { describe, test, expect, vi } from 'vitest'; +import { CseMachineHostPlugin } from '..'; import { CSE_CHANNEL, CSE_MESSAGE_TYPE_SNAPSHOTS, WEB_ID, type CseSnapshot, -} from "@sourceacademy/common-cse-machine"; -import type { IChannel, IConduit } from "@sourceacademy/conductor/conduit"; +} from '@sourceacademy/common-cse-machine'; +import type { IChannel, IConduit } from '@sourceacademy/conductor/conduit'; const makeChannel = () => { let subscriber: ((msg: unknown) => void) | undefined; @@ -31,7 +31,7 @@ const snapshot = (): CseSnapshot => ({ stepIndex: 0, control: [], stash: [], - environments: [{ id: "g", name: "global", parentId: null, bindings: [], isActive: true }], + environments: [{ id: 'g', name: 'global', parentId: null, bindings: [], isActive: true }], }); const validMessage = (snapshots: CseSnapshot[] = [snapshot()], breakpointSteps: number[] = []) => ({ @@ -43,51 +43,50 @@ const validMessage = (snapshots: CseSnapshot[] = [snapshot()], breakpointSteps: // ── Identity / wiring ───────────────────────────────────────────────────────── -describe("plugin identity", () => { - test("id is WEB_ID", () => { +describe('plugin identity', () => { + test('id is WEB_ID', () => { const channel = makeChannel(); const { plugin } = makePlugin(channel); expect(plugin.id).toBe(WEB_ID); }); - test("channelAttach declares the CSE channel", () => { + test('channelAttach declares the CSE channel', () => { expect(CseMachineHostPlugin.channelAttach).toEqual([CSE_CHANNEL]); }); - test("channelAttach contains exactly one channel", () => { + test('channelAttach contains exactly one channel', () => { expect(CseMachineHostPlugin.channelAttach).toHaveLength(1); }); }); // ── Constructor ─────────────────────────────────────────────────────────────── -describe("constructor", () => { - test("throws when no channel is provided", () => { +describe('constructor', () => { + test('throws when no channel is provided', () => { class TestPlugin extends CseMachineHostPlugin { receiveSnapshots = vi.fn(); } expect(() => new TestPlugin({} as IConduit, [])).toThrow( - "CSE channel is required but was not provided.", + 'CSE channel is required but was not provided.', ); }); - test("does not throw when a channel is provided", () => { + test('does not throw when a channel is provided', () => { expect(() => makePlugin(makeChannel())).not.toThrow(); }); }); // ── Valid messages ──────────────────────────────────────────────────────────── -describe("valid messages", () => { - test("receiveSnapshots is called with the snapshot array", () => { +describe('valid messages', () => { + test('receiveSnapshots is called with the snapshot array', () => { const channel = makeChannel(); const { receive } = makePlugin(channel); channel.emit(validMessage()); - expect(receive).toHaveBeenCalledOnce(); - expect(receive).toHaveBeenCalledWith([snapshot()], []); + expect(receive).toHaveBeenCalledExactlyOnceWith([snapshot()], []); }); - test("receiveSnapshots receives multiple snapshots in order", () => { + test('receiveSnapshots receives multiple snapshots in order', () => { const channel = makeChannel(); const { receive } = makePlugin(channel); const snaps = [snapshot(), { ...snapshot(), stepIndex: 1 }, { ...snapshot(), stepIndex: 2 }]; @@ -95,7 +94,7 @@ describe("valid messages", () => { expect(receive).toHaveBeenCalledWith(snaps, []); }); - test("receiveSnapshots is called once per valid message", () => { + test('receiveSnapshots is called once per valid message', () => { const channel = makeChannel(); const { receive } = makePlugin(channel); channel.emit(validMessage()); @@ -103,35 +102,35 @@ describe("valid messages", () => { expect(receive).toHaveBeenCalledTimes(2); }); - test("handles an empty snapshots array", () => { + test('handles an empty snapshots array', () => { const channel = makeChannel(); const { receive } = makePlugin(channel); channel.emit(validMessage([])); expect(receive).toHaveBeenCalledWith([], []); }); - test("preserves snapshot fields passed through the channel", () => { + test('preserves snapshot fields passed through the channel', () => { const channel = makeChannel(); const { receive } = makePlugin(channel); const snap: CseSnapshot = { stepIndex: 4, - control: [{ displayText: "branch", metadata: { instrType: "Branch" } }], - stash: [{ displayValue: "false", label: "bool" }], - environments: [{ id: "g", name: "global", parentId: null, bindings: [], isActive: true }], + control: [{ displayText: 'branch', metadata: { instrType: 'Branch' } }], + stash: [{ displayValue: 'false', label: 'bool' }], + environments: [{ id: 'g', name: 'global', parentId: null, bindings: [], isActive: true }], currentLine: 9, }; channel.emit(validMessage([snap])); expect(receive).toHaveBeenCalledWith([snap], []); }); - test("receiveSnapshots is called with the breakpointSteps array", () => { + test('receiveSnapshots is called with the breakpointSteps array', () => { const channel = makeChannel(); const { receive } = makePlugin(channel); channel.emit(validMessage([snapshot(), { ...snapshot(), stepIndex: 1 }], [1])); expect(receive).toHaveBeenCalledWith([snapshot(), { ...snapshot(), stepIndex: 1 }], [1]); }); - test("defaults breakpointSteps to an empty array when the field is missing from the message", () => { + test('defaults breakpointSteps to an empty array when the field is missing from the message', () => { const channel = makeChannel(); const { receive } = makePlugin(channel); channel.emit({ type: CSE_MESSAGE_TYPE_SNAPSHOTS, snapshots: [snapshot()], totalSteps: 1 }); @@ -141,61 +140,61 @@ describe("valid messages", () => { // ── Invalid / malformed messages ────────────────────────────────────────────── -describe("invalid messages", () => { - test("unknown message type is silently ignored", () => { +describe('invalid messages', () => { + test('unknown message type is silently ignored', () => { const channel = makeChannel(); const { receive } = makePlugin(channel); - channel.emit({ type: "unknown", snapshots: [snapshot()], totalSteps: 1 }); + channel.emit({ type: 'unknown', snapshots: [snapshot()], totalSteps: 1 }); expect(receive).not.toHaveBeenCalled(); }); - test("null message is silently ignored", () => { + test('null message is silently ignored', () => { const channel = makeChannel(); const { receive } = makePlugin(channel); channel.emit(null); expect(receive).not.toHaveBeenCalled(); }); - test("undefined message is silently ignored", () => { + test('undefined message is silently ignored', () => { const channel = makeChannel(); const { receive } = makePlugin(channel); channel.emit(undefined); expect(receive).not.toHaveBeenCalled(); }); - test("snapshots field is not an array — ignored", () => { + test('snapshots field is not an array — ignored', () => { const channel = makeChannel(); const { receive } = makePlugin(channel); - channel.emit({ type: CSE_MESSAGE_TYPE_SNAPSHOTS, snapshots: "bad", totalSteps: 1 }); + channel.emit({ type: CSE_MESSAGE_TYPE_SNAPSHOTS, snapshots: 'bad', totalSteps: 1 }); expect(receive).not.toHaveBeenCalled(); }); - test("totalSteps mismatch — ignored", () => { + test('totalSteps mismatch — ignored', () => { const channel = makeChannel(); const { receive } = makePlugin(channel); channel.emit({ type: CSE_MESSAGE_TYPE_SNAPSHOTS, snapshots: [snapshot()], totalSteps: 99 }); expect(receive).not.toHaveBeenCalled(); }); - test("missing snapshots field — ignored", () => { + test('missing snapshots field — ignored', () => { const channel = makeChannel(); const { receive } = makePlugin(channel); channel.emit({ type: CSE_MESSAGE_TYPE_SNAPSHOTS, totalSteps: 0 }); expect(receive).not.toHaveBeenCalled(); }); - test("empty object — ignored", () => { + test('empty object — ignored', () => { const channel = makeChannel(); const { receive } = makePlugin(channel); channel.emit({}); expect(receive).not.toHaveBeenCalled(); }); - test("invalid messages do not block subsequent valid ones", () => { + test('invalid messages do not block subsequent valid ones', () => { const channel = makeChannel(); const { receive } = makePlugin(channel); channel.emit(null); - channel.emit({ type: "bad" }); + channel.emit({ type: 'bad' }); channel.emit(validMessage()); expect(receive).toHaveBeenCalledOnce(); }); @@ -203,8 +202,8 @@ describe("invalid messages", () => { // ── Re-exports ──────────────────────────────────────────────────────────────── -describe("re-exports from common", () => { - test("CseSnapshot type is re-exported and usable", () => { +describe('re-exports from common', () => { + test('CseSnapshot type is re-exported and usable', () => { // If the import at the top of this file resolves, the re-export works. // This test documents the contract rather than testing runtime behaviour. const snap: CseSnapshot = snapshot(); diff --git a/src/web/cse-machine/src/index.ts b/src/web/cse-machine/src/index.ts index bb1a322..85c675d 100644 --- a/src/web/cse-machine/src/index.ts +++ b/src/web/cse-machine/src/index.ts @@ -4,8 +4,8 @@ import { WEB_ID, type CseSnapshot, type CseSnapshotMessage, -} from "@sourceacademy/common-cse-machine"; -import type { IPlugin, IChannel, IConduit } from "@sourceacademy/conductor/conduit"; +} from '@sourceacademy/common-cse-machine'; +import type { IPlugin, IChannel, IConduit } from '@sourceacademy/conductor/conduit'; // Re-export the protocol types so host apps can depend on a single package for the // web side of the CSE machine. @@ -16,7 +16,7 @@ export type { CseSerializedBinding, CseSerializedEnvFrame, CseSnapshotMessage, -} from "@sourceacademy/common-cse-machine"; +} from '@sourceacademy/common-cse-machine'; /** * Web/host-side CSE machine plugin. @@ -46,7 +46,7 @@ export abstract class CseMachineHostPlugin implements IPlugin { [cseChannel]: IChannel[], ) { if (!cseChannel) { - throw new Error("CSE channel is required but was not provided."); + throw new Error('CSE channel is required but was not provided.'); } (cseChannel as IChannel).subscribe(message => { if ( diff --git a/src/web/data-visualizer/package.json b/src/web/data-visualizer/package.json index 6ad699e..164bec0 100644 --- a/src/web/data-visualizer/package.json +++ b/src/web/data-visualizer/package.json @@ -50,6 +50,7 @@ "react-test-renderer": "^19.0.0", "rollup": "catalog:", "tslib": "catalog:", - "typescript": "catalog:" + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/src/web/data-visualizer/rollup.config.mjs b/src/web/data-visualizer/rollup.config.mjs index 6ea81f3..95cc5d5 100644 --- a/src/web/data-visualizer/rollup.config.mjs +++ b/src/web/data-visualizer/rollup.config.mjs @@ -1,11 +1,11 @@ // @ts-check -import { defineConfig } from "rollup"; -import commonjs from "@rollup/plugin-commonjs"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import replace from "@rollup/plugin-replace"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from 'rollup'; +import commonjs from '@rollup/plugin-commonjs'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import replace from '@rollup/plugin-replace'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; // React, Blueprint, and Konva/react-konva are provided by the host at load time through the // require-provider (see the frontend's `requireProvider`, which already exposes them for other @@ -15,27 +15,27 @@ import typescript from "@rollup/plugin-typescript"; // dispatcher at runtime. Everything else (conductor conduit, the common data-visualizer protocol) // is bundled. wrap.mjs then wraps the CJS output into the host's factory contract. const external = [ - "react", - "react-dom", - "react/jsx-runtime", - "react/jsx-dev-runtime", - "@blueprintjs/core", - "konva", - "react-konva", + 'react', + 'react-dom', + 'react/jsx-runtime', + 'react/jsx-dev-runtime', + '@blueprintjs/core', + 'konva', + 'react-konva', ]; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: { - file: "dist/index.cjs", - format: "cjs", - exports: "default", + file: 'dist/index.cjs', + format: 'cjs', + exports: 'default', }, external, plugins: [ // konva/react-konva pull in CJS deps (e.g. react-is) that branch on `process.env.NODE_ENV` at // module-init time; there's no `process` global in the browser, so replace it at build time. - replace({ preventAssignment: true, "process.env.NODE_ENV": JSON.stringify("production") }), + replace({ preventAssignment: true, 'process.env.NODE_ENV': JSON.stringify('production') }), nodeResolve({ browser: true, preferBuiltins: false }), commonjs(), typescript(), diff --git a/src/web/data-visualizer/src/Config.ts b/src/web/data-visualizer/src/Config.ts index 65ae772..eb3d5d5 100644 --- a/src/web/data-visualizer/src/Config.ts +++ b/src/web/data-visualizer/src/Config.ts @@ -3,8 +3,8 @@ */ export const Config = { StrokeWidth: 2, - Stroke: "white", - Fill: "white", + Stroke: 'white', + Fill: 'white', NWidth: 90, BoxWidth: 45, diff --git a/src/web/data-visualizer/src/DataVisualizerHostPlugin.tsx b/src/web/data-visualizer/src/DataVisualizerHostPlugin.tsx index bab1a7e..a0b5c6e 100644 --- a/src/web/data-visualizer/src/DataVisualizerHostPlugin.tsx +++ b/src/web/data-visualizer/src/DataVisualizerHostPlugin.tsx @@ -3,20 +3,20 @@ import { WEB_ID, type DataVisualizerMessage, type SerializedDataVisualizerRow, -} from "@sourceacademy/common-data-visualizer"; -import type { ITabService, Tab } from "@sourceacademy/common-tabs"; +} from '@sourceacademy/common-data-visualizer'; +import type { ITabService, Tab } from '@sourceacademy/common-tabs'; import { checkIsPluginClass, type IChannel, type IConduit, type IPlugin, -} from "@sourceacademy/conductor/conduit"; -import { createElement, useSyncExternalStore } from "react"; +} from '@sourceacademy/conductor/conduit'; +import { createElement, useSyncExternalStore } from 'react'; -import DataVisualizerView from "./DataVisualizerView"; +import DataVisualizerView from './DataVisualizerView'; /** The side-content tab id used by the host to show/hide the data visualizer tab. */ -const TAB_ID = "data-visualizer"; +const TAB_ID = 'data-visualizer'; /** * The host (browser-side) half of the data visualizer. It listens on the data visualizer channel @@ -45,13 +45,13 @@ export class DataVisualizerHostPlugin implements IPlugin { ) { this.__channel = channel; this.__channel.subscribe(message => { - if (message.type === "rows") { + if (message.type === 'rows') { this.__rows = message.rows; this.__emit(); } }); // Ask the runner to replay the rows it last computed (e.g. tab opened after a run). - this.__channel.send({ type: "request" }); + this.__channel.send({ type: 'request' }); const subscribe = (listener: () => void) => this.subscribe(listener); const getRows = () => this.getRows(); @@ -62,8 +62,8 @@ export class DataVisualizerHostPlugin implements IPlugin { const tab: Tab = { id: TAB_ID, - label: "Data Visualizer", - iconName: "eye-open", + label: 'Data Visualizer', + iconName: 'eye-open', body: createElement(DataVisualizerTab), }; tabService.registerTab(tab); diff --git a/src/web/data-visualizer/src/DataVisualizerView.tsx b/src/web/data-visualizer/src/DataVisualizerView.tsx index 63ecf7b..1d3618f 100644 --- a/src/web/data-visualizer/src/DataVisualizerView.tsx +++ b/src/web/data-visualizer/src/DataVisualizerView.tsx @@ -1,14 +1,14 @@ -import { Button, ButtonGroup, Card, Classes } from "@blueprintjs/core"; -import { useEffect, useState } from "react"; +import { Button, ButtonGroup, Card, Classes } from '@blueprintjs/core'; +import { useEffect, useState } from 'react'; -import type { SerializedDataVisualizerRow } from "@sourceacademy/common-data-visualizer"; -import { Config } from "./Config"; -import { Tree, type ViewMode } from "./tree/Tree"; +import type { SerializedDataVisualizerRow } from '@sourceacademy/common-data-visualizer'; +import { Config } from './Config'; +import { Tree, type ViewMode } from './tree/Tree'; const VIEW_MODES: { mode: ViewMode; label: string }[] = [ - { mode: "classic", label: "Classic" }, - { mode: "binaryTree", label: "Binary Tree" }, - { mode: "generalTree", label: "General Tree" }, + { mode: 'classic', label: 'Classic' }, + { mode: 'binaryTree', label: 'Binary Tree' }, + { mode: 'generalTree', label: 'General Tree' }, ]; // To account for overflow to the left/top due to a backward arrow — matches the old code's @@ -40,7 +40,7 @@ type Props = { */ export default function DataVisualizerView({ rows }: Props) { const [currentStep, setCurrentStep] = useState(0); - const [viewMode, setViewMode] = useState("classic"); + const [viewMode, setViewMode] = useState('classic'); // Mirrors the old code's `DataVisualizer.init(steps => { ...; setCurrentStep(0); })`: every time a // new set of calls arrives (a fresh Run), jump back to the first call. @@ -57,18 +57,18 @@ export default function DataVisualizerView({ rows }: Props) { const active = document.activeElement; const isEditable = active instanceof HTMLElement && - (active.isContentEditable || active.tagName === "INPUT" || active.tagName === "TEXTAREA"); + (active.isContentEditable || active.tagName === 'INPUT' || active.tagName === 'TEXTAREA'); if (isEditable) { return; } - if (event.key === "ArrowLeft") { + if (event.key === 'ArrowLeft') { setCurrentStep(prev => Math.max(0, prev - 1)); - } else if (event.key === "ArrowRight") { + } else if (event.key === 'ArrowRight') { setCurrentStep(prev => Math.min(rows.length - 1, prev + 1)); } } - window.addEventListener("keydown", onKeyDown); - return () => window.removeEventListener("keydown", onKeyDown); + window.addEventListener('keydown', onKeyDown); + return () => window.removeEventListener('keydown', onKeyDown); }, [rows.length]); const onPrevButtonClick = () => setCurrentStep(prev => Math.max(0, prev - 1)); @@ -81,7 +81,7 @@ export default function DataVisualizerView({ rows }: Props) { return (
{rows.length > 0 ? ( -
+
{VIEW_MODES.map(({ mode, label }) => (
{" "} +
{' '}
{hasRunCode ? ( @@ -265,16 +265,16 @@ interface RenderContext { /** Maps a profile token class to the stepper's CSS colour class. */ const TOKEN_CLASS: Record = { - operator: "stepper-operator", - identifier: "stepper-identifier", - literal: "stepper-literal", - conditional: "stepper-conditional-operator", + operator: 'stepper-operator', + identifier: 'stepper-identifier', + literal: 'stepper-literal', + conditional: 'stepper-conditional-operator', }; /** Reads a node property by a (possibly dotted, e.g. `"id.name"`) path, for profile `prop` parts. */ function readNodeProp(node: StepperNode, path: string): unknown { return path - .split(".") + .split('.') .reduce( (value, key) => (value == null ? value : (value as Record)[key]), node, @@ -321,14 +321,14 @@ function FunctionDefinitionPopoverContent({ renderFunctionArguments, }: FunctionDefinitionPopoverContentProps) { return ( -
+
- {" Function definition"} + {' Function definition'}
           
             {renderFunctionArguments(node.params, renderNode, styleWrapper, popoverDepth)}
-            {" => "}
+            {' => '}
             {renderNode(node.body, {
               styleWrapper: styleWrapper ?? (_node => p => p),
               popoverDepth: popoverDepth + 1,
@@ -361,10 +361,10 @@ function ProfileFunctionDefinitionPopover({
   profile,
 }: ProfileFunctionDefinitionPopoverProps) {
   return (
-    
+
- {" Function definition"} + {' Function definition'}
           
             {renderNode(node, {
@@ -393,7 +393,7 @@ function renderNode(
   const renderers = {
     Literal(node: StepperNode) {
       const stringifyLiteralValue = (value: unknown) =>
-        typeof value === "string" ? '"' + value + '"' : value !== null ? String(value) : "null";
+        typeof value === 'string' ? '"' + value + '"' : value !== null ? String(value) : 'null';
       return (
         
           {node.raw ? node.raw : stringifyLiteralValue(node.value)}
@@ -477,7 +477,7 @@ function renderNode(
         (result, item) => (
           
             {result}
-            {", "}
+            {', '}
             {item}
           
         ),
@@ -485,9 +485,9 @@ function renderNode(
       );
       return (
         
-          {"["}
+          {'['}
           {renderedArguments}
-          {"]"}
+          {']'}
         
       );
     },
@@ -497,7 +497,7 @@ function renderNode(
        * corresponding to the name of lambda expression
        */
       function muTermStyleWrapper(targetNode: StepperNode) {
-        if (targetNode.type === "Identifier" && targetNode.name === node.name) {
+        if (targetNode.type === 'Identifier' && targetNode.name === node.name) {
           function addHovering(preprocessed: React.ReactNode): React.ReactNode {
             return (
               
@@ -553,7 +553,7 @@ function renderNode(
       ) : (
         
           {renderFunctionArguments(node.params, renderNode, styleWrapper, popoverDepth)}
-          {" => "}
+          {' => '}
           {renderNode(node.body, {
             styleWrapper: composeStyleWrapper(styleWrapper, muTermStyleWrapper)!,
             popoverDepth: popoverDepth,
@@ -566,12 +566,12 @@ function renderNode(
         styleWrapper: styleWrapper,
         popoverDepth: popoverDepth,
       });
-      if (node.callee.type === "ArrowFunctionExpression" && node.callee.name === undefined) {
+      if (node.callee.type === 'ArrowFunctionExpression' && node.callee.name === undefined) {
         renderedCallee = (
           
-            {"("}
+            {'('}
             {renderedCallee}
-            {")"}
+            {')'}
           
         );
       }
@@ -597,12 +597,12 @@ function renderNode(
       return (
         
           
-            {"if "}
-            {"("}
+            {'if '}
+            {'('}
             
               {renderNode(node.test, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })}
             
-            {") "}
+            {') '}
           
           
             {renderNode(node.consequent, {
@@ -612,7 +612,7 @@ function renderNode(
           
           {node.alternate && (
             
-              {" else "}
+              {' else '}
               {renderNode(node.alternate, {
                 styleWrapper: styleWrapper,
                 popoverDepth: popoverDepth,
@@ -625,23 +625,23 @@ function renderNode(
     ReturnStatement(node: StepperNode) {
       return (
         
-          {"return "}
+          {'return '}
           {node.argument &&
             renderNode(node.argument, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })}
-          {";"}
+          {';'}
         
       );
     },
     BlockStatement(node: StepperNode) {
       return (
         
-          {"{"}
+          {'{'}
           {node.body.map((ast: StepperNode, index: number) => (
-            
+
{renderNode(ast, { styleWrapper, popoverDepth: popoverDepth })}
))} - {"}"} + {'}'} ); }, @@ -649,7 +649,7 @@ function renderNode( return ( {renderNode(node.expression, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} - {";"} + {';'} ); }, @@ -659,7 +659,7 @@ function renderNode( {`function ${node.id.name}`} {renderArguments(node.params)} - {" "} + {' '} {renderNode(node.body, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} @@ -671,11 +671,11 @@ function renderNode( {node.kind} {node.declarations.map((ast: StepperNode, idx: number) => ( - {idx !== 0 && ", "} + {idx !== 0 && ', '} {renderNode(ast, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} ))} - {";"} + {';'} ); }, @@ -683,10 +683,10 @@ function renderNode( return ( {renderNode(node.id, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} - {" = "} + {' = '} {node.init ? renderNode(node.init, { styleWrapper: styleWrapper, popoverDepth: popoverDepth }) - : "undefined"} + : 'undefined'} ); }, @@ -702,7 +702,7 @@ function renderNode( styleWrapper: StyleWrapper | undefined, popoverDepth: number, ) => { - if (!nodes) return "()"; + if (!nodes) return '()'; const args: React.ReactNode[] = nodes.map(arg => renderNodeFn(arg, { styleWrapper: styleWrapper ?? (_node => p => p), @@ -713,7 +713,7 @@ function renderNode( (result, item) => ( {result} - {", "} + {', '} {item} ), @@ -722,9 +722,9 @@ function renderNode( if (args.length !== 1) { renderedArguments = ( - {"("} + {'('} {renderedArguments} - {")"} + {')'} ); } @@ -732,7 +732,7 @@ function renderNode( }; const renderArguments = (nodes: StepperNode[] | undefined) => { - if (!nodes) return "()"; + if (!nodes) return '()'; const args: React.ReactNode[] = nodes.map(arg => renderNode(arg, { styleWrapper: styleWrapper, popoverDepth: popoverDepth }), ); @@ -740,7 +740,7 @@ function renderNode( (result, item) => ( {result} - {", "} + {', '} {item} ), @@ -748,9 +748,9 @@ function renderNode( ); renderedArguments = ( - {"("} + {'('} {renderedArguments} - {")"} + {')'} ); return renderedArguments; @@ -769,22 +769,22 @@ function renderNode( }); const cls = (c?: string) => (c ? TOKEN_CLASS[c] : undefined); const renderPart = (part: SyntaxTemplatePart, key: number): React.ReactNode => { - if (typeof part === "string") return {part}; - if ("token" in part) + if (typeof part === 'string') return {part}; + if ('token' in part) return ( {part.token} ); - if ("prop" in part) { + if ('prop' in part) { const value = readNodeProp(node, part.prop); return ( - {value == null ? "" : String(value)} + {value == null ? '' : String(value)} ); } - if ("child" in part) { + if ('child' in part) { const child = node[part.child] as StepperNode | null | undefined; return ( @@ -792,7 +792,7 @@ function renderNode( ); } - if ("list" in part) { + if ('list' in part) { const items = (node[part.list] as StepperNode[] | undefined) ?? []; if (items.length === 0) return null; return ( @@ -807,19 +807,19 @@ function renderNode( ); } - if ("block" in part) { + if ('block' in part) { const items = (node[part.block] as StepperNode[] | undefined) ?? []; return ( {items.map((item, i) => ( -
+
{renderNode(item, childContext({}))}
))} ); } - if ("lines" in part) { + if ('lines' in part) { const items = (node[part.lines] as StepperNode[] | undefined) ?? []; return ( @@ -829,7 +829,7 @@ function renderNode( ); } - if ("when" in part) { + if ('when' in part) { return node[part.when] ? ( {part.parts.map((p, i) => renderPart(p, i))} ) : null; @@ -853,7 +853,7 @@ function renderNode( // Recursive hovering: identifiers in the body that refer back to this function (by name) are // themselves wrapped in the same popover, so a recursive definition stays explorable. const muTermWrapper: StyleWrapper = (targetNode: StepperNode) => - targetNode.type === "Identifier" && targetNode.name === funcName + targetNode.type === 'Identifier' && targetNode.name === funcName ? (preprocessed: React.ReactNode) => ( - {"("} + {'('} {result} - {")"} + {')'} ); } @@ -964,7 +964,7 @@ function CustomASTRenderer( // Match by stable node id rather than object identity, which does not survive // serialization across the runner/host channel. if (marker.redexId !== undefined && marker.redexId === node.nodeId) { - const Wrapper = node.type === "FunctionDeclaration" ? "div" : "span"; + const Wrapper = node.type === 'FunctionDeclaration' ? 'div' : 'span'; returnNode = {returnNode}; } }); @@ -1018,7 +1018,7 @@ function expressionNeedsParenthesis( ); } - if (!("operator" in node) || !("operator" in parentNode)) { + if (!('operator' in node) || !('operator' in parentNode)) { return false; } @@ -1026,14 +1026,14 @@ function expressionNeedsParenthesis( // Not a `LogicalExpression` or `BinaryExpression` return false; } - if (node.operator === "**" && parentNode.operator === "**") { + if (node.operator === '**' && parentNode.operator === '**') { // Exponentiation operator has right-to-left associativity return !isRightHand; } if ( nodePrecedence === 13 && parentNodePrecedence === 13 && - (node.operator === "??" || parentNode.operator === "??") + (node.operator === '??' || parentNode.operator === '??') ) { return true; } @@ -1046,31 +1046,31 @@ function expressionNeedsParenthesis( : nodeOperatorPrecedence <= parentNodeOperatorPrecedence; } const OPERATOR_PRECEDENCE = { - "||": 2, - "??": 3, - "&&": 4, - "|": 5, - "^": 6, - "&": 7, - "==": 8, - "!=": 8, - "===": 8, - "!==": 8, - "<": 9, - ">": 9, - "<=": 9, - ">=": 9, + '||': 2, + '??': 3, + '&&': 4, + '|': 5, + '^': 6, + '&': 7, + '==': 8, + '!=': 8, + '===': 8, + '!==': 8, + '<': 9, + '>': 9, + '<=': 9, + '>=': 9, in: 9, instanceof: 9, - "<<": 10, - ">>": 10, - ">>>": 10, - "+": 11, - "-": 11, - "*": 12, - "%": 12, - "/": 12, - "**": 13, + '<<': 10, + '>>': 10, + '>>>': 10, + '+': 11, + '-': 11, + '*': 12, + '%': 12, + '/': 12, + '**': 13, }; const NEEDS_PARENTHESES = 17; const EXPRESSIONS_PRECEDENCE = { diff --git a/src/web/stepper/src/index.ts b/src/web/stepper/src/index.ts index 0b00d54..21dc6b5 100644 --- a/src/web/stepper/src/index.ts +++ b/src/web/stepper/src/index.ts @@ -1,4 +1,4 @@ // The host's external-plugin loader imports the bundle and calls its default export as a factory // (`default(requireProvider) => PluginClass`); the build wraps this CommonJS output accordingly (see // wrap.mjs). So the entry default-exports the plugin class, which becomes the bundle's `module.exports`. -export { StepperHostPlugin as default } from "./StepperHostPlugin"; +export { StepperHostPlugin as default } from './StepperHostPlugin'; diff --git a/src/web/stepper/src/styles.ts b/src/web/stepper/src/styles.ts index 8f7046a..a6eb8b4 100644 --- a/src/web/stepper/src/styles.ts +++ b/src/web/stepper/src/styles.ts @@ -123,14 +123,14 @@ const STEPPER_CSS = ` } `; -const STYLE_ELEMENT_ID = "__sa_stepper_styles"; +const STYLE_ELEMENT_ID = '__sa_stepper_styles'; /** Injects (or refreshes) the stepper stylesheet in the document. No-op outside the browser. */ export function injectStepperStyles(): void { - if (typeof document === "undefined") return; + if (typeof document === 'undefined') return; let style = document.getElementById(STYLE_ELEMENT_ID) as HTMLStyleElement | null; if (style === null) { - style = document.createElement("style"); + style = document.createElement('style'); style.id = STYLE_ELEMENT_ID; document.head.appendChild(style); } diff --git a/src/web/stepper/wrap.mjs b/src/web/stepper/wrap.mjs index 7371e96..0904009 100644 --- a/src/web/stepper/wrap.mjs +++ b/src/web/stepper/wrap.mjs @@ -7,11 +7,11 @@ // in `export default require => { ...cjs...; return module.exports }`, shadowing `require`/`module`/ // `exports` so the bundle's external `require(...)` calls hit the provider. (Mirrors plugins #25's // build transform.) Output is written to dist/index.mjs (the file the frontend serves & imports). -import { readFile, writeFile } from "node:fs/promises"; +import { readFile, writeFile } from 'node:fs/promises'; -const cjsUrl = new URL("./dist/index.cjs", import.meta.url); -const mjsUrl = new URL("./dist/index.mjs", import.meta.url); +const cjsUrl = new URL('./dist/index.cjs', import.meta.url); +const mjsUrl = new URL('./dist/index.mjs', import.meta.url); -const cjs = await readFile(cjsUrl, "utf-8"); +const cjs = await readFile(cjsUrl, 'utf-8'); const wrapped = `export default require => {let module = {exports: {}}; let exports = module.exports;\n${cjs}\n; return module.exports;}`; await writeFile(mjsUrl, wrapped); diff --git a/src/web/test/rollup.config.mjs b/src/web/test/rollup.config.mjs index ebd33ea..b3ad615 100644 --- a/src/web/test/rollup.config.mjs +++ b/src/web/test/rollup.config.mjs @@ -1,24 +1,23 @@ // @ts-check -import { defineConfig } from "rollup"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; -import commonjs from "@rollup/plugin-commonjs"; +import { defineConfig } from 'rollup'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; +import commonjs from '@rollup/plugin-commonjs'; export default defineConfig({ - input: "src/index.tsx", + input: 'src/index.tsx', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], plugins: [nodeResolve(), commonjs(), typescript(), terser()], - external: ["react", "react-dom", "react/jsx-runtime"], + external: ['react', 'react-dom', 'react/jsx-runtime'], }); - diff --git a/src/web/test/src/index.tsx b/src/web/test/src/index.tsx index add94be..3bfc501 100644 --- a/src/web/test/src/index.tsx +++ b/src/web/test/src/index.tsx @@ -1,11 +1,11 @@ -import { CHANNEL_ID, WEB_ID, type TestMessage } from "@sourceacademy/common-test"; +import { CHANNEL_ID, WEB_ID, type TestMessage } from '@sourceacademy/common-test'; import { type IPlugin, type IChannel, type IConduit, checkIsPluginClass, -} from "@sourceacademy/conductor/conduit"; -import type { ITabService, Tab } from "@sourceacademy/common-tabs"; +} from '@sourceacademy/conductor/conduit'; +import type { ITabService, Tab } from '@sourceacademy/common-tabs'; function Element({ workspaceLocation, @@ -19,11 +19,11 @@ function Element({ return (
This is a test tab. The location is {workspaceLocation} @@ -56,12 +56,12 @@ export abstract class TestPlugin implements IPlugin { this.__testChannel.subscribe(message => { console.log(message); }); - this.__testChannel.send("ping"); + this.__testChannel.send('ping'); const tab = { - id: "test-tab", - iconName: "airplane", + id: 'test-tab', + iconName: 'airplane', body: , - label: "Test Tab", + label: 'Test Tab', disabled: false, } satisfies Tab; tabService.registerTab(tab); diff --git a/vitest.config.ts b/vitest.config.ts index e83a239..8095c32 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,11 +1,11 @@ -import { defineConfig } from "vitest/config"; +import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { coverage: { - provider: "istanbul", - reporter: "lcov", - exclude: ["**/dist"], + provider: 'istanbul', + reporter: 'lcov', + exclude: ['**/dist'], }, }, }); diff --git a/yarn.lock b/yarn.lock index dbd1e86..978b7ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1448,6 +1448,7 @@ __metadata: rollup: "catalog:" tslib: "catalog:" typescript: "catalog:" + vitest: "catalog:" languageName: unknown linkType: soft @@ -1540,6 +1541,7 @@ __metadata: "@sourceacademy/plugin-directory": "source-academy/plugin-directory#main" "@types/node": "npm:^25.9.1" "@vitest/coverage-istanbul": "npm:^4.1.9" + "@vitest/eslint-plugin": "npm:^1.6.26" "@yarnpkg/types": "npm:^4.0.1" commander: "npm:^15.0.0" eslint: "npm:^10.0.3" @@ -1757,6 +1759,7 @@ __metadata: rollup: "catalog:" tslib: "catalog:" typescript: "catalog:" + vitest: "catalog:" peerDependencies: "@blueprintjs/core": ">=5" konva: ">=9" @@ -2007,6 +2010,19 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/project-service@npm:8.66.0": + version: 8.66.0 + resolution: "@typescript-eslint/project-service@npm:8.66.0" + dependencies: + "@typescript-eslint/tsconfig-utils": "npm:^8.66.0" + "@typescript-eslint/types": "npm:^8.66.0" + debug: "npm:^4.4.3" + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/1b8129da708262104691091ac09f67fca2dd877d99090472fd5f0cf86b05755cb3df11d98ed003e775864b9f015c01b429fd5553adbb8a94dd047ff83d4ad6b3 + languageName: node + linkType: hard + "@typescript-eslint/scope-manager@npm:8.65.0": version: 8.65.0 resolution: "@typescript-eslint/scope-manager@npm:8.65.0" @@ -2017,6 +2033,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:8.66.0, @typescript-eslint/scope-manager@npm:^8.58.0": + version: 8.66.0 + resolution: "@typescript-eslint/scope-manager@npm:8.66.0" + dependencies: + "@typescript-eslint/types": "npm:8.66.0" + "@typescript-eslint/visitor-keys": "npm:8.66.0" + checksum: 10c0/247709b8712295650b8145050c3d566a71ebdca0a10477766cf369f35c9f7fdf7aa62ebb3b3c05fad3ae13c867196c9ff04ede7b1e7b8a6f60acf550207095d4 + languageName: node + linkType: hard + "@typescript-eslint/tsconfig-utils@npm:8.65.0, @typescript-eslint/tsconfig-utils@npm:^8.65.0": version: 8.65.0 resolution: "@typescript-eslint/tsconfig-utils@npm:8.65.0" @@ -2026,6 +2052,15 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/tsconfig-utils@npm:8.66.0, @typescript-eslint/tsconfig-utils@npm:^8.66.0": + version: 8.66.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.66.0" + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/1267e1f0adf822062c3917d717f0d1e463e46fdf2d9d0afd6b99b4498f27e63597296cf7ed4a5fe24c3311afb5458d54ccd369d5acee0f51017814384cfd1983 + languageName: node + linkType: hard + "@typescript-eslint/type-utils@npm:8.65.0": version: 8.65.0 resolution: "@typescript-eslint/type-utils@npm:8.65.0" @@ -2049,6 +2084,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:8.66.0, @typescript-eslint/types@npm:^8.66.0": + version: 8.66.0 + resolution: "@typescript-eslint/types@npm:8.66.0" + checksum: 10c0/cc3f77a9a4e2ee997f0b660e8d31d2e45f3b4a16000f8d4349c85cd50b6a902b71272055116cc01f9cfae063a9d31e8b533e6051334fb345fdf753805014c6b5 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:8.65.0": version: 8.65.0 resolution: "@typescript-eslint/typescript-estree@npm:8.65.0" @@ -2068,6 +2110,25 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:8.66.0": + version: 8.66.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.66.0" + dependencies: + "@typescript-eslint/project-service": "npm:8.66.0" + "@typescript-eslint/tsconfig-utils": "npm:8.66.0" + "@typescript-eslint/types": "npm:8.66.0" + "@typescript-eslint/visitor-keys": "npm:8.66.0" + debug: "npm:^4.4.3" + minimatch: "npm:^10.2.2" + semver: "npm:^7.7.3" + tinyglobby: "npm:^0.2.15" + ts-api-utils: "npm:^2.5.0" + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/4a755666340ea6f7d329efc402bf0628bf3085c26192b8b346e3641f1b1804c09254a705d0c29b85e2a730258e4675b1678b9f0d95cbbc3b6f5aefb95e6ff67f + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:8.65.0": version: 8.65.0 resolution: "@typescript-eslint/utils@npm:8.65.0" @@ -2083,6 +2144,21 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:^8.58.0": + version: 8.66.0 + resolution: "@typescript-eslint/utils@npm:8.66.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.9.1" + "@typescript-eslint/scope-manager": "npm:8.66.0" + "@typescript-eslint/types": "npm:8.66.0" + "@typescript-eslint/typescript-estree": "npm:8.66.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/8bce52462ac7c42da7ec3967f1f30c0c9ae527471c6d95de9376a3b4e584bfbcceae7c69cf7df5d081481ccfde5f0682112e05ed4f8704916028a23024e965a0 + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:8.65.0": version: 8.65.0 resolution: "@typescript-eslint/visitor-keys@npm:8.65.0" @@ -2093,6 +2169,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:8.66.0": + version: 8.66.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.66.0" + dependencies: + "@typescript-eslint/types": "npm:8.66.0" + eslint-visitor-keys: "npm:^5.0.0" + checksum: 10c0/12fd739fa3da099145ed21cc3b81fce050a725117639f3e162be51d807b38d553688bdb62b3251c9bb2c9361bfcb4aa68beb0e0e3885620713aa6ede7ce231c7 + languageName: node + linkType: hard + "@vitest/coverage-istanbul@npm:^4.1.9": version: 4.1.10 resolution: "@vitest/coverage-istanbul@npm:4.1.10" @@ -2113,6 +2199,28 @@ __metadata: languageName: node linkType: hard +"@vitest/eslint-plugin@npm:^1.6.26": + version: 1.6.26 + resolution: "@vitest/eslint-plugin@npm:1.6.26" + dependencies: + "@typescript-eslint/scope-manager": "npm:^8.58.0" + "@typescript-eslint/utils": "npm:^8.58.0" + peerDependencies: + "@typescript-eslint/eslint-plugin": "*" + eslint: ">=8.57.0" + typescript: ">=5.0.0" + vitest: "*" + peerDependenciesMeta: + "@typescript-eslint/eslint-plugin": + optional: true + typescript: + optional: true + vitest: + optional: true + checksum: 10c0/e1dd39f6ae26f7e53261f95ee5ec1cf6b9a8b9eabf0b908d3cf589bff30f86051ab6b86364dc3fdb11fce3684e90255173988a56091f778c4f77e3d75afbd62e + languageName: node + linkType: hard + "@vitest/expect@npm:4.1.10": version: 4.1.10 resolution: "@vitest/expect@npm:4.1.10" From 4744f8dfc6a21f1de31af4749cc8bcca2e024f61 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Thu, 6 Aug 2026 22:22:00 +0800 Subject: [PATCH 07/29] Exclude test files in the typescript plugin --- src/common/autocomplete/rollup.config.mjs | 2 +- src/common/cse-machine/rollup.config.mjs | 2 +- src/common/data-visualizer/rollup.config.mjs | 2 +- src/common/module-loader/rollup.config.mjs | 2 +- src/common/stepper/rollup.config.mjs | 2 +- src/common/tabs/rollup.config.mjs | 2 +- src/common/test/rollup.config.mjs | 2 +- src/runner/autocomplete/rollup.config.mjs | 2 +- src/runner/cse-machine/rollup.config.mjs | 2 +- src/runner/data-visualizer/rollup.config.mjs | 2 +- src/runner/module-loader/rollup.config.mjs | 2 +- src/runner/remoteExecution/rollup.config.mjs | 2 +- src/runner/stepper/rollup.config.mjs | 2 +- src/runner/test/rollup.config.mjs | 2 +- src/web/autocomplete/rollup.config.mjs | 2 +- src/web/cse-machine/rollup.config.mjs | 2 +- src/web/data-visualizer/rollup.config.mjs | 2 +- src/web/module-loader/rollup.config.mjs | 2 +- src/web/stepper/rollup.config.mjs | 2 +- src/web/test/rollup.config.mjs | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/common/autocomplete/rollup.config.mjs b/src/common/autocomplete/rollup.config.mjs index 9a8d5e0..f9ac364 100644 --- a/src/common/autocomplete/rollup.config.mjs +++ b/src/common/autocomplete/rollup.config.mjs @@ -17,5 +17,5 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), typescript(), terser()], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], }); diff --git a/src/common/cse-machine/rollup.config.mjs b/src/common/cse-machine/rollup.config.mjs index 9a8d5e0..f9ac364 100644 --- a/src/common/cse-machine/rollup.config.mjs +++ b/src/common/cse-machine/rollup.config.mjs @@ -17,5 +17,5 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), typescript(), terser()], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], }); diff --git a/src/common/data-visualizer/rollup.config.mjs b/src/common/data-visualizer/rollup.config.mjs index 9a8d5e0..f9ac364 100644 --- a/src/common/data-visualizer/rollup.config.mjs +++ b/src/common/data-visualizer/rollup.config.mjs @@ -17,5 +17,5 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), typescript(), terser()], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], }); diff --git a/src/common/module-loader/rollup.config.mjs b/src/common/module-loader/rollup.config.mjs index 9a8d5e0..f9ac364 100644 --- a/src/common/module-loader/rollup.config.mjs +++ b/src/common/module-loader/rollup.config.mjs @@ -17,5 +17,5 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), typescript(), terser()], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], }); diff --git a/src/common/stepper/rollup.config.mjs b/src/common/stepper/rollup.config.mjs index 9a8d5e0..f9ac364 100644 --- a/src/common/stepper/rollup.config.mjs +++ b/src/common/stepper/rollup.config.mjs @@ -17,5 +17,5 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), typescript(), terser()], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], }); diff --git a/src/common/tabs/rollup.config.mjs b/src/common/tabs/rollup.config.mjs index 9a8d5e0..f9ac364 100644 --- a/src/common/tabs/rollup.config.mjs +++ b/src/common/tabs/rollup.config.mjs @@ -17,5 +17,5 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), typescript(), terser()], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], }); diff --git a/src/common/test/rollup.config.mjs b/src/common/test/rollup.config.mjs index 9a8d5e0..f9ac364 100644 --- a/src/common/test/rollup.config.mjs +++ b/src/common/test/rollup.config.mjs @@ -17,5 +17,5 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), typescript(), terser()], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], }); diff --git a/src/runner/autocomplete/rollup.config.mjs b/src/runner/autocomplete/rollup.config.mjs index 711c6d5..fc55229 100644 --- a/src/runner/autocomplete/rollup.config.mjs +++ b/src/runner/autocomplete/rollup.config.mjs @@ -17,6 +17,6 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), typescript(), terser()], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], external: [/^@sourceacademy\/conductor(?:\/|$)/], }); diff --git a/src/runner/cse-machine/rollup.config.mjs b/src/runner/cse-machine/rollup.config.mjs index 9a8d5e0..f9ac364 100644 --- a/src/runner/cse-machine/rollup.config.mjs +++ b/src/runner/cse-machine/rollup.config.mjs @@ -17,5 +17,5 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), typescript(), terser()], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], }); diff --git a/src/runner/data-visualizer/rollup.config.mjs b/src/runner/data-visualizer/rollup.config.mjs index 9a8d5e0..f9ac364 100644 --- a/src/runner/data-visualizer/rollup.config.mjs +++ b/src/runner/data-visualizer/rollup.config.mjs @@ -17,5 +17,5 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), typescript(), terser()], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], }); diff --git a/src/runner/module-loader/rollup.config.mjs b/src/runner/module-loader/rollup.config.mjs index 9a8d5e0..f9ac364 100644 --- a/src/runner/module-loader/rollup.config.mjs +++ b/src/runner/module-loader/rollup.config.mjs @@ -17,5 +17,5 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), typescript(), terser()], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], }); diff --git a/src/runner/remoteExecution/rollup.config.mjs b/src/runner/remoteExecution/rollup.config.mjs index 5770caf..5bb0583 100644 --- a/src/runner/remoteExecution/rollup.config.mjs +++ b/src/runner/remoteExecution/rollup.config.mjs @@ -18,5 +18,5 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), typescript(), terser()], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], }); diff --git a/src/runner/stepper/rollup.config.mjs b/src/runner/stepper/rollup.config.mjs index 9a8d5e0..f9ac364 100644 --- a/src/runner/stepper/rollup.config.mjs +++ b/src/runner/stepper/rollup.config.mjs @@ -17,5 +17,5 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), typescript(), terser()], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], }); diff --git a/src/runner/test/rollup.config.mjs b/src/runner/test/rollup.config.mjs index 9a8d5e0..f9ac364 100644 --- a/src/runner/test/rollup.config.mjs +++ b/src/runner/test/rollup.config.mjs @@ -17,5 +17,5 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), typescript(), terser()], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], }); diff --git a/src/web/autocomplete/rollup.config.mjs b/src/web/autocomplete/rollup.config.mjs index 7aadcd2..c81a80d 100644 --- a/src/web/autocomplete/rollup.config.mjs +++ b/src/web/autocomplete/rollup.config.mjs @@ -18,6 +18,6 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), commonjs(), typescript(), terser()], + plugins: [nodeResolve(), commonjs(), typescript({ exclude: ['**/__tests__/**'] }), terser()], external: ['react', 'react-dom', 'react/jsx-runtime', /^@sourceacademy\/conductor(?:\/|$)/], }); diff --git a/src/web/cse-machine/rollup.config.mjs b/src/web/cse-machine/rollup.config.mjs index 9a8d5e0..f9ac364 100644 --- a/src/web/cse-machine/rollup.config.mjs +++ b/src/web/cse-machine/rollup.config.mjs @@ -17,5 +17,5 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), typescript(), terser()], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], }); diff --git a/src/web/data-visualizer/rollup.config.mjs b/src/web/data-visualizer/rollup.config.mjs index 95cc5d5..7aa35c5 100644 --- a/src/web/data-visualizer/rollup.config.mjs +++ b/src/web/data-visualizer/rollup.config.mjs @@ -38,7 +38,7 @@ export default defineConfig({ replace({ preventAssignment: true, 'process.env.NODE_ENV': JSON.stringify('production') }), nodeResolve({ browser: true, preferBuiltins: false }), commonjs(), - typescript(), + typescript({ exclude: ['**/__tests__/**'] }), terser(), ], }); diff --git a/src/web/module-loader/rollup.config.mjs b/src/web/module-loader/rollup.config.mjs index df7d85b..c3808d7 100644 --- a/src/web/module-loader/rollup.config.mjs +++ b/src/web/module-loader/rollup.config.mjs @@ -18,6 +18,6 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), commonjs(), typescript(), terser()], + plugins: [nodeResolve(), commonjs(), typescript({ exclude: ['**/__tests__/**'] }), terser()], external: ['react', 'react-dom', 'react/jsx-runtime'], }); diff --git a/src/web/stepper/rollup.config.mjs b/src/web/stepper/rollup.config.mjs index 1826fc7..a9e96af 100644 --- a/src/web/stepper/rollup.config.mjs +++ b/src/web/stepper/rollup.config.mjs @@ -29,7 +29,7 @@ export default defineConfig({ plugins: [ nodeResolve({ browser: true, preferBuiltins: false }), commonjs(), - typescript(), + typescript({ exclude: ['**/__tests__/**'] }), terser(), ], }); diff --git a/src/web/test/rollup.config.mjs b/src/web/test/rollup.config.mjs index b3ad615..b0e0d89 100644 --- a/src/web/test/rollup.config.mjs +++ b/src/web/test/rollup.config.mjs @@ -18,6 +18,6 @@ export default defineConfig({ format: 'esm', }, ], - plugins: [nodeResolve(), commonjs(), typescript(), terser()], + plugins: [nodeResolve(), commonjs(), typescript({ exclude: ['**/__tests__/**'] }), terser()], external: ['react', 'react-dom', 'react/jsx-runtime'], }); From 07146c32fc4ca11d9a39f0d83b6df55d6e818c5d Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Thu, 6 Aug 2026 22:28:27 +0800 Subject: [PATCH 08/29] Fix type errors for subst visualizer --- eslint.config.mjs | 2 +- package.json | 2 +- src/web/stepper/src/SubstVisualizer.tsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index a08dc73..bf12d33 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -50,7 +50,7 @@ export default defineConfig([ }, }, { - files: ['src/**/*.js'], + files: ['**/*.{js,cjs}'], rules: { '@typescript-eslint/no-require-imports': 'off', }, diff --git a/package.json b/package.json index 5002c86..598fb2e 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "scripts": { "build": "tsx lib/build.ts", "dev": "tsx lib/build.ts --watch", - "runall": "yarn workspaces foreach -A --exclude \"@sourceacademy/plugins-workspace\" run", + "runall": "yarn workspaces foreach -p -A --exclude \"@sourceacademy/plugins-workspace\" run", "test": "vitest run", "test-coverage": "vitest run --coverage", "test:dev": "vitest", diff --git a/src/web/stepper/src/SubstVisualizer.tsx b/src/web/stepper/src/SubstVisualizer.tsx index 73163bb..13ef68a 100644 --- a/src/web/stepper/src/SubstVisualizer.tsx +++ b/src/web/stepper/src/SubstVisualizer.tsx @@ -304,10 +304,10 @@ interface FunctionDefinitionPopoverContentProps { node: StepperNode; styleWrapper: StyleWrapper | undefined; popoverDepth: number; - renderNode: (node: StepperNode, context: RenderContext) => React.ReactNode; + renderNode: (node: StepperNode | undefined | null, context: RenderContext) => React.ReactNode; renderFunctionArguments: ( nodes: StepperNode[], - renderNodeFn: (node: StepperNode, context: RenderContext) => React.ReactNode, + renderNodeFn: (node: StepperNode | undefined | null, context: RenderContext) => React.ReactNode, styleWrapper: StyleWrapper | undefined, popoverDepth: number, ) => React.ReactNode; From 4715dd2173db7868bf2a6f8837614fbabaae9331 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Thu, 6 Aug 2026 22:35:32 +0800 Subject: [PATCH 09/29] Provide some extra workspace scripts --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 598fb2e..ebb5874 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "scripts": { "build": "tsx lib/build.ts", "dev": "tsx lib/build.ts --watch", - "runall": "yarn workspaces foreach -p -A --exclude \"@sourceacademy/plugins-workspace\" run", + "run:all": "yarn workspaces foreach -pt -A --exclude \"@sourceacademy/plugins-workspace\" run", + "run:since": "yarn workspaces foreach -pt --since --exclude \"@sourceacademy/plugins-workspace\" run", "test": "vitest run", "test-coverage": "vitest run --coverage", "test:dev": "vitest", From 7c254aba6d90cac063dd017cb2f7e1c7c0b99858 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Thu, 6 Aug 2026 22:51:58 +0800 Subject: [PATCH 10/29] Fix the linting issue --- src/runner/module-loader/src/__tests__/runner.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/runner/module-loader/src/__tests__/runner.test.ts b/src/runner/module-loader/src/__tests__/runner.test.ts index fd0fe6d..ddb2355 100644 --- a/src/runner/module-loader/src/__tests__/runner.test.ts +++ b/src/runner/module-loader/src/__tests__/runner.test.ts @@ -212,6 +212,7 @@ describe('requestModule', () => { await plugin.requestModule('module'); expect.fail('Error should have been thrown'); } catch (err) { + // eslint-disable-next-line vitest/no-conditional-expect expect(err).toBeInstanceOf(Error); } }); @@ -262,7 +263,7 @@ describe('requestModule', () => { tabs: ['Tab1'], }; }); - const { plugin, pluginObj, registerPlugin } = makePlugin(channel); + const { plugin, pluginObj } = makePlugin(channel); await plugin.requestModule('chart'); await expect(plugin.requestModule('chart')).resolves.toBe(pluginObj); }); From 417b4a33d536babb4bfc7e8fa2d229576307a18d Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Sat, 8 Aug 2026 15:31:13 +0800 Subject: [PATCH 11/29] Use more ergonomic utilities in the build script --- lib/build.ts | 76 +++++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/lib/build.ts b/lib/build.ts index fb31e64..41b65a3 100644 --- a/lib/build.ts +++ b/lib/build.ts @@ -1,15 +1,20 @@ -import { Command } from 'commander'; -import packageJSON from '../package.json' with { type: 'json' }; +import { exec, spawn } from 'child_process'; import fs from 'fs/promises'; -import { spawn, exec } from 'child_process'; +import { createRequire } from 'module'; import pathlib from 'path'; import type { IPluginDefinition } from '@sourceacademy/plugin-directory/dist/types/IPluginDefinition.js'; +import { Command } from 'commander'; +import packageJSON from '../package.json' with { type: 'json' }; interface WorkspaceEntry { location: string; name: string; } +interface Manifest { + type: 'installable' | 'external'; +} + /** * Use `yarn workspaces list --json` to gather all the workspaces in the repository */ @@ -30,12 +35,9 @@ async function getWorkspaces() { } const repoRoot = pathlib.resolve(import.meta.dirname, '..'); - -const program = new Command(); -interface Manifest { - type: 'installable' | 'external'; -} - +// Create a `require` function to use node's resolution algorithm to find +// the entry point of plugins +const require = createRequire(import.meta.url); const pluginDirectory: Record = {}; /** @@ -46,11 +48,12 @@ const pluginDirectory: Record = {}; * await generateManifest(); * This will generate the manifest files for each plugin and update the changeset config to ignore non-installable plugins. */ -async function generateManifest() { +export async function generateManifest() { const globalManifest: Record> = {}; // Create dist directory if it doesn't exist - await fs.mkdir('dist', { recursive: true }); + const distDirectory = pathlib.join(repoRoot, 'dist'); + await fs.mkdir(distDirectory, { recursive: true }); // The ignore list is used to keep track of non-installable plugins, which should be ignored in changesets to prevent unnecessary version bumps and releases. const ignoreList: string[] = []; @@ -62,7 +65,7 @@ async function generateManifest() { // Don't process the root workspace if (workspaceName === packageJSON.name) return; - const pkgMatch = /@sourceacademy\/(.+?)-(.+)/.exec(workspaceName); + const pkgMatch = /@sourceacademy\/(\w+?)-(.+)/.exec(workspaceName); if (!pkgMatch) throw new Error(`Invalid package name ${workspaceName}`); const [, pluginType, pluginName] = pkgMatch; @@ -71,8 +74,9 @@ async function generateManifest() { globalManifest[pluginType] = {}; } - const manifestFilePath = pathlib.join(repoRoot, location, 'manifest.json'); - const pkgJsonPath = pathlib.join(repoRoot, location, 'package.json'); + const workspacePath = pathlib.resolve(repoRoot, location); + const manifestFilePath = pathlib.join(workspacePath, 'manifest.json'); + const pkgJsonPath = pathlib.join(workspacePath, 'package.json'); const [{ default: manifestFile }, { default: packageJSONFile }] = await Promise.all([ import(`file://${manifestFilePath}`, { with: { type: 'json' } }), @@ -91,17 +95,12 @@ async function generateManifest() { description: packageJSONFile.description, resolutions: {}, }; + // Use the plugin's own declared entry point rather than assuming ".js": external // plugins can emit ".mjs" (e.g. the stepper's wrap.mjs produces real ESM for native // `import()`), and a mismatch here silently 404s for anyone resolving the plugin. - // package.json is untyped JSON, so guard against main/module being non-string (e.g. an - // object, from a more complex export config) before handing them to pathlib.basename. - const mainEntry = - typeof packageJSONFile.main === 'string' ? packageJSONFile.main : undefined; - const moduleEntry = - typeof packageJSONFile.module === 'string' ? packageJSONFile.module : undefined; - - const entryFile = pathlib.basename(mainEntry ?? moduleEntry ?? 'index.js'); + const entryFile = pathlib.basename(require.resolve(workspacePath)); + pluginDirectory[pluginName].resolutions[pluginType] = pathlib.posix.join( '.', pluginType, @@ -114,9 +113,9 @@ async function generateManifest() { // Write the manifest for each plugin type to the dist folder. await Promise.all( - Object.entries(globalManifest).map(async ([pluginType, manifest]) => { + Object.entries(globalManifest).map(([pluginType, manifest]) => { const outpath = pathlib.join(repoRoot, 'dist', `${pluginType}.json`); - await fs.writeFile(outpath, JSON.stringify(manifest, null, 2)); + return fs.writeFile(outpath, JSON.stringify(manifest, null, 2)); }), ); @@ -197,14 +196,14 @@ async function copyDistFiles(globalManifest: Record Date: Sat, 8 Aug 2026 15:31:31 +0800 Subject: [PATCH 12/29] Add eslint-plugin-import and remove tsx --- eslint.config.mjs | 34 ++- package.json | 2 +- yarn.lock | 626 +++++++++++++++++++++++++--------------------- 3 files changed, 368 insertions(+), 294 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index d28aacc..b470246 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,16 +1,42 @@ // @ts-check -import eslintConfigPrettierFlat from 'eslint-config-prettier/flat'; +import vitest from '@vitest/eslint-plugin'; import { defineConfig } from 'eslint/config'; +import eslintConfigPrettierFlat from 'eslint-config-prettier/flat'; +import * as importPlugin from 'eslint-plugin-import-x'; import tseslint from 'typescript-eslint'; -import vitest from '@vitest/eslint-plugin'; export default defineConfig([ { ignores: ['**/dist', '**/node_modules', '**/coverage'], }, - tseslint.configs.recommended, eslintConfigPrettierFlat, + { + plugins: { + import: importPlugin + }, + rules: { + 'import/first': 'warn', + 'import/newline-after-import': 'warn', + 'import/no-duplicates': ['warn', { 'prefer-inline': false }], + 'import/no-useless-path-segments': 'error', + 'import/order': [ + 'warn', + { + groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'], + named: { + import: true, + types: 'types-last' + }, + alphabetize: { + order: 'asc', + orderImportKind: 'asc' + }, + } + ], + } + }, + tseslint.configs.recommended, { files: ['**/*.{ts,tsx}'], languageOptions: { @@ -45,8 +71,10 @@ export default defineConfig([ rules: { '@typescript-eslint/no-explicit-any': 'off', + 'vitest/prefer-describe-function-title': 'warn', 'vitest/prefer-import-in-mock': 'error', 'vitest/prefer-vi-mocked': 'warn', + 'vitest/valid-title': ['warn', { ignoreTypeOfDescribeName: true }], }, }, { diff --git a/package.json b/package.json index d67e84c..5161f34 100644 --- a/package.json +++ b/package.json @@ -31,8 +31,8 @@ "commander": "^15.0.0", "eslint": "^10.0.3", "eslint-config-prettier": "^10.1.8", + "eslint-plugin-import-x": "^4.17.1", "prettier": "^3.8.1", - "tsx": "^4.22.3", "typescript": "catalog:", "typescript-eslint": "^8.57.1", "vitest": "catalog:" diff --git a/yarn.lock b/yarn.lock index 978b7ce..b2a847a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -502,188 +502,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/aix-ppc64@npm:0.28.1" - conditions: os=aix & cpu=ppc64 - languageName: node - linkType: hard - -"@esbuild/android-arm64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/android-arm64@npm:0.28.1" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - -"@esbuild/android-arm@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/android-arm@npm:0.28.1" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - -"@esbuild/android-x64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/android-x64@npm:0.28.1" - conditions: os=android & cpu=x64 - languageName: node - linkType: hard - -"@esbuild/darwin-arm64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/darwin-arm64@npm:0.28.1" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@esbuild/darwin-x64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/darwin-x64@npm:0.28.1" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@esbuild/freebsd-arm64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/freebsd-arm64@npm:0.28.1" - conditions: os=freebsd & cpu=arm64 - languageName: node - linkType: hard - -"@esbuild/freebsd-x64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/freebsd-x64@npm:0.28.1" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - -"@esbuild/linux-arm64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/linux-arm64@npm:0.28.1" - conditions: os=linux & cpu=arm64 - languageName: node - linkType: hard - -"@esbuild/linux-arm@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/linux-arm@npm:0.28.1" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@esbuild/linux-ia32@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/linux-ia32@npm:0.28.1" - conditions: os=linux & cpu=ia32 - languageName: node - linkType: hard - -"@esbuild/linux-loong64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/linux-loong64@npm:0.28.1" - conditions: os=linux & cpu=loong64 - languageName: node - linkType: hard - -"@esbuild/linux-mips64el@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/linux-mips64el@npm:0.28.1" - conditions: os=linux & cpu=mips64el - languageName: node - linkType: hard - -"@esbuild/linux-ppc64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/linux-ppc64@npm:0.28.1" - conditions: os=linux & cpu=ppc64 - languageName: node - linkType: hard - -"@esbuild/linux-riscv64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/linux-riscv64@npm:0.28.1" - conditions: os=linux & cpu=riscv64 - languageName: node - linkType: hard - -"@esbuild/linux-s390x@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/linux-s390x@npm:0.28.1" - conditions: os=linux & cpu=s390x - languageName: node - linkType: hard - -"@esbuild/linux-x64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/linux-x64@npm:0.28.1" - conditions: os=linux & cpu=x64 - languageName: node - linkType: hard - -"@esbuild/netbsd-arm64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/netbsd-arm64@npm:0.28.1" - conditions: os=netbsd & cpu=arm64 - languageName: node - linkType: hard - -"@esbuild/netbsd-x64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/netbsd-x64@npm:0.28.1" - conditions: os=netbsd & cpu=x64 - languageName: node - linkType: hard - -"@esbuild/openbsd-arm64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/openbsd-arm64@npm:0.28.1" - conditions: os=openbsd & cpu=arm64 - languageName: node - linkType: hard - -"@esbuild/openbsd-x64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/openbsd-x64@npm:0.28.1" - conditions: os=openbsd & cpu=x64 - languageName: node - linkType: hard - -"@esbuild/openharmony-arm64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/openharmony-arm64@npm:0.28.1" - conditions: os=openharmony & cpu=arm64 - languageName: node - linkType: hard - -"@esbuild/sunos-x64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/sunos-x64@npm:0.28.1" - conditions: os=sunos & cpu=x64 - languageName: node - linkType: hard - -"@esbuild/win32-arm64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/win32-arm64@npm:0.28.1" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"@esbuild/win32-ia32@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/win32-ia32@npm:0.28.1" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"@esbuild/win32-x64@npm:0.28.1": - version: 0.28.1 - resolution: "@esbuild/win32-x64@npm:0.28.1" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - "@eslint-community/eslint-utils@npm:^4.8.0, @eslint-community/eslint-utils@npm:^4.9.1": version: 4.9.1 resolution: "@eslint-community/eslint-utils@npm:4.9.1" @@ -1546,8 +1364,8 @@ __metadata: commander: "npm:^15.0.0" eslint: "npm:^10.0.3" eslint-config-prettier: "npm:^10.1.8" + eslint-plugin-import-x: "npm:^4.17.1" prettier: "npm:^3.8.1" - tsx: "npm:^4.22.3" typescript: "catalog:" typescript-eslint: "npm:^8.57.1" vitest: "catalog:" @@ -2084,7 +1902,7 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:8.66.0, @typescript-eslint/types@npm:^8.66.0": +"@typescript-eslint/types@npm:8.66.0, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.66.0": version: 8.66.0 resolution: "@typescript-eslint/types@npm:8.66.0" checksum: 10c0/cc3f77a9a4e2ee997f0b660e8d31d2e45f3b4a16000f8d4349c85cd50b6a902b71272055116cc01f9cfae063a9d31e8b533e6051334fb345fdf753805014c6b5 @@ -2179,6 +1997,164 @@ __metadata: languageName: node linkType: hard +"@unrs/resolver-binding-android-arm-eabi@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-android-arm-eabi@npm:1.12.2" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@unrs/resolver-binding-android-arm64@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-android-arm64@npm:1.12.2" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@unrs/resolver-binding-darwin-arm64@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-darwin-arm64@npm:1.12.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@unrs/resolver-binding-darwin-x64@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-darwin-x64@npm:1.12.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@unrs/resolver-binding-freebsd-x64@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-freebsd-x64@npm:1.12.2" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.12.2" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-arm-musleabihf@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-linux-arm-musleabihf@npm:1.12.2" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-arm64-gnu@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-linux-arm64-gnu@npm:1.12.2" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-arm64-musl@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-linux-arm64-musl@npm:1.12.2" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-loong64-gnu@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-linux-loong64-gnu@npm:1.12.2" + conditions: os=linux & cpu=loong64 & libc=glibc + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-loong64-musl@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-linux-loong64-musl@npm:1.12.2" + conditions: os=linux & cpu=loong64 & libc=musl + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-ppc64-gnu@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-linux-ppc64-gnu@npm:1.12.2" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-riscv64-gnu@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-linux-riscv64-gnu@npm:1.12.2" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-riscv64-musl@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-linux-riscv64-musl@npm:1.12.2" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-s390x-gnu@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-linux-s390x-gnu@npm:1.12.2" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-x64-gnu@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-linux-x64-gnu@npm:1.12.2" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@unrs/resolver-binding-linux-x64-musl@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-linux-x64-musl@npm:1.12.2" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@unrs/resolver-binding-openharmony-arm64@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-openharmony-arm64@npm:1.12.2" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@unrs/resolver-binding-wasm32-wasi@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-wasm32-wasi@npm:1.12.2" + dependencies: + "@emnapi/core": "npm:1.10.0" + "@emnapi/runtime": "npm:1.10.0" + "@napi-rs/wasm-runtime": "npm:^1.1.4" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@unrs/resolver-binding-win32-arm64-msvc@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-win32-arm64-msvc@npm:1.12.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@unrs/resolver-binding-win32-ia32-msvc@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-win32-ia32-msvc@npm:1.12.2" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@unrs/resolver-binding-win32-x64-msvc@npm:1.12.2": + version: 1.12.2 + resolution: "@unrs/resolver-binding-win32-x64-msvc@npm:1.12.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@vitest/coverage-istanbul@npm:^4.1.9": version: 4.1.10 resolution: "@vitest/coverage-istanbul@npm:4.1.10" @@ -2427,6 +2403,15 @@ __metadata: languageName: node linkType: hard +"brace-expansion@npm:^5.0.8": + version: 5.0.9 + resolution: "brace-expansion@npm:5.0.9" + dependencies: + balanced-match: "npm:^4.0.2" + checksum: 10c0/3dea38884a1c3c8b1c9c44a7402a0c76fca460f70cffb3127242b0b4cbf4472019e022ade021eec44838ff19f1dac2625dfd11dd459d7e1e055b0698a8d52fec + languageName: node + linkType: hard + "braces@npm:^3.0.3": version: 3.0.3 resolution: "braces@npm:3.0.3" @@ -2548,6 +2533,13 @@ __metadata: languageName: node linkType: hard +"comment-parser@npm:^1.4.1": + version: 1.4.7 + resolution: "comment-parser@npm:1.4.7" + checksum: 10c0/09a8e6cc4a9f92e8828bbd8819d8b025cb1bf03d8d611e372627380f55b6401bb0009cf1b7940a028794f150891bfe855185b94173eb89f14b824e847335278d + languageName: node + linkType: hard + "commondir@npm:^1.0.1": version: 1.0.1 resolution: "commondir@npm:1.0.1" @@ -2598,7 +2590,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:^4.1.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.4.3": +"debug@npm:^4.1.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.4.1, debug@npm:^4.4.3": version: 4.4.3 resolution: "debug@npm:4.4.3" dependencies: @@ -2719,95 +2711,6 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:~0.28.0": - version: 0.28.1 - resolution: "esbuild@npm:0.28.1" - dependencies: - "@esbuild/aix-ppc64": "npm:0.28.1" - "@esbuild/android-arm": "npm:0.28.1" - "@esbuild/android-arm64": "npm:0.28.1" - "@esbuild/android-x64": "npm:0.28.1" - "@esbuild/darwin-arm64": "npm:0.28.1" - "@esbuild/darwin-x64": "npm:0.28.1" - "@esbuild/freebsd-arm64": "npm:0.28.1" - "@esbuild/freebsd-x64": "npm:0.28.1" - "@esbuild/linux-arm": "npm:0.28.1" - "@esbuild/linux-arm64": "npm:0.28.1" - "@esbuild/linux-ia32": "npm:0.28.1" - "@esbuild/linux-loong64": "npm:0.28.1" - "@esbuild/linux-mips64el": "npm:0.28.1" - "@esbuild/linux-ppc64": "npm:0.28.1" - "@esbuild/linux-riscv64": "npm:0.28.1" - "@esbuild/linux-s390x": "npm:0.28.1" - "@esbuild/linux-x64": "npm:0.28.1" - "@esbuild/netbsd-arm64": "npm:0.28.1" - "@esbuild/netbsd-x64": "npm:0.28.1" - "@esbuild/openbsd-arm64": "npm:0.28.1" - "@esbuild/openbsd-x64": "npm:0.28.1" - "@esbuild/openharmony-arm64": "npm:0.28.1" - "@esbuild/sunos-x64": "npm:0.28.1" - "@esbuild/win32-arm64": "npm:0.28.1" - "@esbuild/win32-ia32": "npm:0.28.1" - "@esbuild/win32-x64": "npm:0.28.1" - dependenciesMeta: - "@esbuild/aix-ppc64": - optional: true - "@esbuild/android-arm": - optional: true - "@esbuild/android-arm64": - optional: true - "@esbuild/android-x64": - optional: true - "@esbuild/darwin-arm64": - optional: true - "@esbuild/darwin-x64": - optional: true - "@esbuild/freebsd-arm64": - optional: true - "@esbuild/freebsd-x64": - optional: true - "@esbuild/linux-arm": - optional: true - "@esbuild/linux-arm64": - optional: true - "@esbuild/linux-ia32": - optional: true - "@esbuild/linux-loong64": - optional: true - "@esbuild/linux-mips64el": - optional: true - "@esbuild/linux-ppc64": - optional: true - "@esbuild/linux-riscv64": - optional: true - "@esbuild/linux-s390x": - optional: true - "@esbuild/linux-x64": - optional: true - "@esbuild/netbsd-arm64": - optional: true - "@esbuild/netbsd-x64": - optional: true - "@esbuild/openbsd-arm64": - optional: true - "@esbuild/openbsd-x64": - optional: true - "@esbuild/openharmony-arm64": - optional: true - "@esbuild/sunos-x64": - optional: true - "@esbuild/win32-arm64": - optional: true - "@esbuild/win32-ia32": - optional: true - "@esbuild/win32-x64": - optional: true - bin: - esbuild: bin/esbuild - checksum: 10c0/29cd456a79ce35ac2c7e05fe871330416b2c395c045d849653f843e51378d6e0d6e774d6dcd01b35f4e83238a29bf8decd04fcd34b3780c589a250b21e5f92bb - languageName: node - linkType: hard - "escalade@npm:^3.2.0": version: 3.2.0 resolution: "escalade@npm:3.2.0" @@ -2840,6 +2743,47 @@ __metadata: languageName: node linkType: hard +"eslint-import-context@npm:^0.1.9": + version: 0.1.9 + resolution: "eslint-import-context@npm:0.1.9" + dependencies: + get-tsconfig: "npm:^4.10.1" + stable-hash-x: "npm:^0.2.0" + peerDependencies: + unrs-resolver: ^1.0.0 + peerDependenciesMeta: + unrs-resolver: + optional: true + checksum: 10c0/07851103443b70af681c5988e2702e681ff9b956e055e11d4bd9b2322847fa0d9e8da50c18fc7cb1165106b043f34fbd0384d7011c239465c4645c52132e56f3 + languageName: node + linkType: hard + +"eslint-plugin-import-x@npm:^4.17.1": + version: 4.17.1 + resolution: "eslint-plugin-import-x@npm:4.17.1" + dependencies: + "@typescript-eslint/types": "npm:^8.56.0" + comment-parser: "npm:^1.4.1" + debug: "npm:^4.4.1" + eslint-import-context: "npm:^0.1.9" + is-glob: "npm:^4.0.3" + minimatch: "npm:^9.0.3 || ^10.1.2" + semver: "npm:^7.7.2" + stable-hash-x: "npm:^0.2.0" + unrs-resolver: "npm:^1.9.2" + peerDependencies: + "@typescript-eslint/utils": ^8.56.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + eslint-import-resolver-node: "*" + peerDependenciesMeta: + "@typescript-eslint/utils": + optional: true + eslint-import-resolver-node: + optional: true + checksum: 10c0/4ea2ace22c3d805f2253063912211c438055f112ca9a2124df262311744975fc96443e4526fd80eef0acec7845a48fb6d1da444a4c4b0ff9786927fe2755f2a7 + languageName: node + linkType: hard + "eslint-scope@npm:^9.1.2": version: 9.1.2 resolution: "eslint-scope@npm:9.1.2" @@ -3189,6 +3133,15 @@ __metadata: languageName: node linkType: hard +"get-tsconfig@npm:^4.10.1": + version: 4.14.1 + resolution: "get-tsconfig@npm:4.14.1" + dependencies: + resolve-pkg-maps: "npm:^1.0.0" + checksum: 10c0/c5e5d8fd298be9bc554b8ca3708a76732c27f632cd9b902c1d41b23ad1f4b6fefbe4dbdecd2fee84994d1ebf46e8cc50dd575fcff1b7ffafcdf6e2e0809a0337 + languageName: node + linkType: hard + "glob-parent@npm:^5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" @@ -3779,6 +3732,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.3 || ^10.1.2": + version: 10.2.6 + resolution: "minimatch@npm:10.2.6" + dependencies: + brace-expansion: "npm:^5.0.8" + checksum: 10c0/4559a836243b98bd4d17ea9f7edae698717c76399eea7be374f3737f33164e4907f19e9726891ddeb122f750a5a7fa80d2ac43e851d6e5984dc4ff42ec127d3a + languageName: node + linkType: hard + "minipass@npm:^7.0.4, minipass@npm:^7.1.2": version: 7.1.3 resolution: "minipass@npm:7.1.3" @@ -3825,6 +3787,15 @@ __metadata: languageName: node linkType: hard +"napi-postinstall@npm:^0.3.4": + version: 0.3.4 + resolution: "napi-postinstall@npm:0.3.4" + bin: + napi-postinstall: lib/cli.js + checksum: 10c0/b33d64150828bdade3a5d07368a8b30da22ee393f8dd8432f1b9e5486867be21c84ec443dd875dd3ef3c7401a079a7ab7e2aa9d3538a889abbcd96495d5104fe + languageName: node + linkType: hard + "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -4333,6 +4304,13 @@ __metadata: languageName: node linkType: hard +"resolve-pkg-maps@npm:^1.0.0": + version: 1.0.0 + resolution: "resolve-pkg-maps@npm:1.0.0" + checksum: 10c0/fb8f7bbe2ca281a73b7ef423a1cbc786fb244bd7a95cbe5c3fba25b27d327150beca8ba02f622baea65919a57e061eb5005204daa5f93ed590d9b77463a567ab + languageName: node + linkType: hard + "resolve@npm:^1.22.1": version: 1.22.12 resolution: "resolve@npm:1.22.12" @@ -4562,7 +4540,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.7.3": +"semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.7.2, semver@npm:^7.7.3": version: 7.8.5 resolution: "semver@npm:7.8.5" bin: @@ -4684,6 +4662,13 @@ __metadata: languageName: node linkType: hard +"stable-hash-x@npm:^0.2.0": + version: 0.2.0 + resolution: "stable-hash-x@npm:0.2.0" + checksum: 10c0/c757df58366ee4bb266a9486b8932eab7c1ba730469eaf4b68d2dee404814e9f84089c44c9b5205f8c7d99a0ab036cce2af69139ce5ed44b635923c011a8aea8 + languageName: node + linkType: hard + "stackback@npm:0.0.2": version: 0.0.2 resolution: "stackback@npm:0.0.2" @@ -4841,21 +4826,6 @@ __metadata: languageName: node linkType: hard -"tsx@npm:^4.22.3": - version: 4.23.5 - resolution: "tsx@npm:4.23.5" - dependencies: - esbuild: "npm:~0.28.0" - fsevents: "npm:~2.3.3" - dependenciesMeta: - fsevents: - optional: true - bin: - tsx: dist/cli.mjs - checksum: 10c0/caed1b05eac5fbbb6f8a1ca8b22ae157b74caaba14cc781b023f8baaaa990fa8dfa60a4252eabda1a68c4dcaade3411013998fa001580a2de4ac1f0d78c80c92 - languageName: node - linkType: hard - "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -4928,6 +4898,82 @@ __metadata: languageName: node linkType: hard +"unrs-resolver@npm:^1.9.2": + version: 1.12.2 + resolution: "unrs-resolver@npm:1.12.2" + dependencies: + "@unrs/resolver-binding-android-arm-eabi": "npm:1.12.2" + "@unrs/resolver-binding-android-arm64": "npm:1.12.2" + "@unrs/resolver-binding-darwin-arm64": "npm:1.12.2" + "@unrs/resolver-binding-darwin-x64": "npm:1.12.2" + "@unrs/resolver-binding-freebsd-x64": "npm:1.12.2" + "@unrs/resolver-binding-linux-arm-gnueabihf": "npm:1.12.2" + "@unrs/resolver-binding-linux-arm-musleabihf": "npm:1.12.2" + "@unrs/resolver-binding-linux-arm64-gnu": "npm:1.12.2" + "@unrs/resolver-binding-linux-arm64-musl": "npm:1.12.2" + "@unrs/resolver-binding-linux-loong64-gnu": "npm:1.12.2" + "@unrs/resolver-binding-linux-loong64-musl": "npm:1.12.2" + "@unrs/resolver-binding-linux-ppc64-gnu": "npm:1.12.2" + "@unrs/resolver-binding-linux-riscv64-gnu": "npm:1.12.2" + "@unrs/resolver-binding-linux-riscv64-musl": "npm:1.12.2" + "@unrs/resolver-binding-linux-s390x-gnu": "npm:1.12.2" + "@unrs/resolver-binding-linux-x64-gnu": "npm:1.12.2" + "@unrs/resolver-binding-linux-x64-musl": "npm:1.12.2" + "@unrs/resolver-binding-openharmony-arm64": "npm:1.12.2" + "@unrs/resolver-binding-wasm32-wasi": "npm:1.12.2" + "@unrs/resolver-binding-win32-arm64-msvc": "npm:1.12.2" + "@unrs/resolver-binding-win32-ia32-msvc": "npm:1.12.2" + "@unrs/resolver-binding-win32-x64-msvc": "npm:1.12.2" + napi-postinstall: "npm:^0.3.4" + dependenciesMeta: + "@unrs/resolver-binding-android-arm-eabi": + optional: true + "@unrs/resolver-binding-android-arm64": + optional: true + "@unrs/resolver-binding-darwin-arm64": + optional: true + "@unrs/resolver-binding-darwin-x64": + optional: true + "@unrs/resolver-binding-freebsd-x64": + optional: true + "@unrs/resolver-binding-linux-arm-gnueabihf": + optional: true + "@unrs/resolver-binding-linux-arm-musleabihf": + optional: true + "@unrs/resolver-binding-linux-arm64-gnu": + optional: true + "@unrs/resolver-binding-linux-arm64-musl": + optional: true + "@unrs/resolver-binding-linux-loong64-gnu": + optional: true + "@unrs/resolver-binding-linux-loong64-musl": + optional: true + "@unrs/resolver-binding-linux-ppc64-gnu": + optional: true + "@unrs/resolver-binding-linux-riscv64-gnu": + optional: true + "@unrs/resolver-binding-linux-riscv64-musl": + optional: true + "@unrs/resolver-binding-linux-s390x-gnu": + optional: true + "@unrs/resolver-binding-linux-x64-gnu": + optional: true + "@unrs/resolver-binding-linux-x64-musl": + optional: true + "@unrs/resolver-binding-openharmony-arm64": + optional: true + "@unrs/resolver-binding-wasm32-wasi": + optional: true + "@unrs/resolver-binding-win32-arm64-msvc": + optional: true + "@unrs/resolver-binding-win32-ia32-msvc": + optional: true + "@unrs/resolver-binding-win32-x64-msvc": + optional: true + checksum: 10c0/ddc27f6d920eabdafeac0077ebff9fd799c895cea025751dc17b360bf9be7c93c471fafebf65f205eec476f90d7daa36aef889d47362b2dd4705d68852bcfea4 + languageName: node + linkType: hard + "update-browserslist-db@npm:^1.2.3": version: 1.2.3 resolution: "update-browserslist-db@npm:1.2.3" From 23739f12e39fddef35728116faa1de8f9ed77dd5 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Sat, 8 Aug 2026 15:31:46 +0800 Subject: [PATCH 13/29] Run linting --- src/common/autocomplete/rollup.config.mjs | 2 +- src/common/cse-machine/rollup.config.mjs | 2 +- .../cse-machine/src/__tests__/common.test.ts | 20 +++++++------- src/common/data-visualizer/rollup.config.mjs | 2 +- src/common/module-loader/rollup.config.mjs | 2 +- src/common/stepper/rollup.config.mjs | 2 +- src/common/tabs/rollup.config.mjs | 2 +- src/common/test/rollup.config.mjs | 2 +- src/common/test/src/__tests__/common.test.ts | 2 +- src/runner/autocomplete/rollup.config.mjs | 2 +- .../autocomplete/src/__tests__/runner.test.ts | 2 +- src/runner/autocomplete/src/index.ts | 16 ++++++------ src/runner/cse-machine/rollup.config.mjs | 2 +- .../cse-machine/src/__tests__/runner.test.ts | 4 +-- src/runner/cse-machine/src/index.ts | 2 +- src/runner/data-visualizer/rollup.config.mjs | 2 +- .../src/__tests__/runner.test.ts | 4 +-- src/runner/module-loader/rollup.config.mjs | 2 +- .../src/__tests__/runner.test.ts | 8 +++--- src/runner/remoteExecution/index.ts | 2 +- src/runner/remoteExecution/rollup.config.mjs | 2 +- src/runner/stepper/rollup.config.mjs | 2 +- .../stepper/src/__tests__/runner.test.ts | 2 +- src/runner/test/rollup.config.mjs | 2 +- src/runner/test/src/__tests__/runner.test.ts | 8 +++--- src/web/autocomplete/rollup.config.mjs | 4 +-- src/web/autocomplete/src/index.ts | 16 ++++++------ src/web/cse-machine/rollup.config.mjs | 2 +- src/web/cse-machine/src/__tests__/web.test.ts | 4 +-- src/web/cse-machine/src/index.ts | 2 +- src/web/data-visualizer/rollup.config.mjs | 2 +- .../src/DataVisualizerView.tsx | 2 +- .../src/__tests__/DataVisualizerView.test.tsx | 12 ++++----- .../src/__tests__/classify.test.ts | 2 +- .../src/__tests__/tree.test.ts | 26 +++++++++---------- .../src/__tests__/treeViews.test.ts | 24 ++++++++--------- .../src/tree/BinaryTreeDrawer.tsx | 2 +- .../src/tree/ClassicDrawer.tsx | 2 +- .../src/tree/GeneralTreeDrawer.tsx | 2 +- src/web/module-loader/rollup.config.mjs | 4 +-- .../module-loader/src/__tests__/web.test.ts | 4 +-- src/web/module-loader/src/index.ts | 4 +-- src/web/stepper/rollup.config.mjs | 2 +- src/web/stepper/src/SubstVisualizer.tsx | 14 +++++----- src/web/test/rollup.config.mjs | 4 +-- src/web/test/src/index.tsx | 6 ++--- 46 files changed, 119 insertions(+), 119 deletions(-) diff --git a/src/common/autocomplete/rollup.config.mjs b/src/common/autocomplete/rollup.config.mjs index f9ac364..875363f 100644 --- a/src/common/autocomplete/rollup.config.mjs +++ b/src/common/autocomplete/rollup.config.mjs @@ -1,9 +1,9 @@ // @ts-check -import { defineConfig } from 'rollup'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/common/cse-machine/rollup.config.mjs b/src/common/cse-machine/rollup.config.mjs index f9ac364..875363f 100644 --- a/src/common/cse-machine/rollup.config.mjs +++ b/src/common/cse-machine/rollup.config.mjs @@ -1,9 +1,9 @@ // @ts-check -import { defineConfig } from 'rollup'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/common/cse-machine/src/__tests__/common.test.ts b/src/common/cse-machine/src/__tests__/common.test.ts index 9595e5d..4a4bd29 100644 --- a/src/common/cse-machine/src/__tests__/common.test.ts +++ b/src/common/cse-machine/src/__tests__/common.test.ts @@ -1,16 +1,16 @@ -import { describe, it, expect } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { CSE_CHANNEL, CSE_DIRECTORY_ID, CSE_MESSAGE_TYPE_SNAPSHOTS, RUNNER_ID, WEB_ID, + type CseSerializedBinding, + type CseSerializedEnvFrame, + type CseSerializedInstruction, + type CseSerializedValue, type CseSnapshot, type CseSnapshotMessage, - type CseSerializedValue, - type CseSerializedInstruction, - type CseSerializedEnvFrame, - type CseSerializedBinding, } from '../index'; // ── Constants ───────────────────────────────────────────────────────────────── @@ -96,7 +96,7 @@ describe('structured-clone safety', () => { // ── CseSnapshotMessage ──────────────────────────────────────────────────────── -describe('CseSnapshotMessage', () => { +describe(CseSnapshotMessage, () => { it('totalSteps matches snapshots.length for a batch', () => { const snapshots: CseSnapshot[] = [ { stepIndex: 0, control: [], stash: [], environments: [] }, @@ -198,7 +198,7 @@ describe('CseSnapshot optional fields', () => { // ── CseSerializedValue ──────────────────────────────────────────────────────── -describe('CseSerializedValue', () => { +describe(CseSerializedValue, () => { it('accepts minimal value with displayValue and label only', () => { const v: CseSerializedValue = { displayValue: 'true', label: 'bool' }; expect(v.tag).toBeUndefined(); @@ -219,7 +219,7 @@ describe('CseSerializedValue', () => { // ── CseSerializedInstruction ────────────────────────────────────────────────── -describe('CseSerializedInstruction', () => { +describe(CseSerializedInstruction, () => { it('accepts minimal instruction with displayText only', () => { const instr: CseSerializedInstruction = { displayText: 'pop' }; expect(instr.tag).toBeUndefined(); @@ -242,7 +242,7 @@ describe('CseSerializedInstruction', () => { // ── CseSerializedEnvFrame ───────────────────────────────────────────────────── -describe('CseSerializedEnvFrame', () => { +describe(CseSerializedEnvFrame, () => { it('root frame has null parentId', () => { const frame: CseSerializedEnvFrame = { id: 'g', @@ -318,7 +318,7 @@ describe('CseSerializedEnvFrame', () => { // ── CseSerializedBinding ────────────────────────────────────────────────────── -describe('CseSerializedBinding', () => { +describe(CseSerializedBinding, () => { it('isConst is optional and defaults to undefined', () => { const b: CseSerializedBinding = { name: 'x', diff --git a/src/common/data-visualizer/rollup.config.mjs b/src/common/data-visualizer/rollup.config.mjs index f9ac364..875363f 100644 --- a/src/common/data-visualizer/rollup.config.mjs +++ b/src/common/data-visualizer/rollup.config.mjs @@ -1,9 +1,9 @@ // @ts-check -import { defineConfig } from 'rollup'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/common/module-loader/rollup.config.mjs b/src/common/module-loader/rollup.config.mjs index f9ac364..875363f 100644 --- a/src/common/module-loader/rollup.config.mjs +++ b/src/common/module-loader/rollup.config.mjs @@ -1,9 +1,9 @@ // @ts-check -import { defineConfig } from 'rollup'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/common/stepper/rollup.config.mjs b/src/common/stepper/rollup.config.mjs index f9ac364..875363f 100644 --- a/src/common/stepper/rollup.config.mjs +++ b/src/common/stepper/rollup.config.mjs @@ -1,9 +1,9 @@ // @ts-check -import { defineConfig } from 'rollup'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/common/tabs/rollup.config.mjs b/src/common/tabs/rollup.config.mjs index f9ac364..875363f 100644 --- a/src/common/tabs/rollup.config.mjs +++ b/src/common/tabs/rollup.config.mjs @@ -1,9 +1,9 @@ // @ts-check -import { defineConfig } from 'rollup'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/common/test/rollup.config.mjs b/src/common/test/rollup.config.mjs index f9ac364..875363f 100644 --- a/src/common/test/rollup.config.mjs +++ b/src/common/test/rollup.config.mjs @@ -1,9 +1,9 @@ // @ts-check -import { defineConfig } from 'rollup'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/common/test/src/__tests__/common.test.ts b/src/common/test/src/__tests__/common.test.ts index 5a55a9a..ccd8ddc 100644 --- a/src/common/test/src/__tests__/common.test.ts +++ b/src/common/test/src/__tests__/common.test.ts @@ -1,5 +1,5 @@ +import { expect, test } from 'vitest'; import { CHANNEL_ID } from '..'; -import { test, expect } from 'vitest'; test('should have a valid channel id', () => { expect(CHANNEL_ID).toBe('test'); diff --git a/src/runner/autocomplete/rollup.config.mjs b/src/runner/autocomplete/rollup.config.mjs index fc55229..99119e2 100644 --- a/src/runner/autocomplete/rollup.config.mjs +++ b/src/runner/autocomplete/rollup.config.mjs @@ -1,9 +1,9 @@ // @ts-check -import { defineConfig } from 'rollup'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/runner/autocomplete/src/__tests__/runner.test.ts b/src/runner/autocomplete/src/__tests__/runner.test.ts index 160a18a..e521284 100644 --- a/src/runner/autocomplete/src/__tests__/runner.test.ts +++ b/src/runner/autocomplete/src/__tests__/runner.test.ts @@ -1,4 +1,3 @@ -import { BaseAutoCompleteRunnerPlugin } from '..'; import { AUTOCOMPLETE_CHANNEL_ID, RUNNER_PLUGIN_ID, @@ -17,6 +16,7 @@ import { type IRpcMessage, } from '@sourceacademy/conductor/conduit'; import { afterEach, expect, test, vi } from 'vitest'; +import { BaseAutoCompleteRunnerPlugin } from '..'; afterEach(() => { vi.useRealTimers(); diff --git a/src/runner/autocomplete/src/index.ts b/src/runner/autocomplete/src/index.ts index 4c3086d..4b96260 100644 --- a/src/runner/autocomplete/src/index.ts +++ b/src/runner/autocomplete/src/index.ts @@ -1,11 +1,3 @@ -import { - makeRpc, - type IChannel, - type IConduit, - type IPlugin, - type IRpcMessage, -} from '@sourceacademy/conductor/conduit'; - import { AUTOCOMPLETE_CHANNEL_ID, RUNNER_PLUGIN_ID, @@ -19,6 +11,14 @@ import { type TransferredModeFunction, type TransferredSyntaxHighlightData, } from '@sourceacademy/common-autocomplete'; +import { + makeRpc, + type IChannel, + type IConduit, + type IPlugin, + type IRpcMessage, +} from '@sourceacademy/conductor/conduit'; + const transferModeFunction = (name: keyof ModeRpc, fn: ModeFunction): TransferredModeFunction => typeof fn === 'function' ? { rpc: name } : fn; diff --git a/src/runner/cse-machine/rollup.config.mjs b/src/runner/cse-machine/rollup.config.mjs index f9ac364..875363f 100644 --- a/src/runner/cse-machine/rollup.config.mjs +++ b/src/runner/cse-machine/rollup.config.mjs @@ -1,9 +1,9 @@ // @ts-check -import { defineConfig } from 'rollup'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/runner/cse-machine/src/__tests__/runner.test.ts b/src/runner/cse-machine/src/__tests__/runner.test.ts index 02c95d5..983a9d6 100644 --- a/src/runner/cse-machine/src/__tests__/runner.test.ts +++ b/src/runner/cse-machine/src/__tests__/runner.test.ts @@ -1,5 +1,3 @@ -import { describe, test, expect, vi } from 'vitest'; -import { CseMachinePlugin } from '..'; import { CSE_CHANNEL, CSE_MESSAGE_TYPE_SNAPSHOTS, @@ -7,6 +5,8 @@ import { type CseSnapshot, } from '@sourceacademy/common-cse-machine'; import type { IChannel, IConduit } from '@sourceacademy/conductor/conduit'; +import { describe, expect, test, vi } from 'vitest'; +import { CseMachinePlugin } from '..'; const makeChannel = () => { const send = vi.fn(); diff --git a/src/runner/cse-machine/src/index.ts b/src/runner/cse-machine/src/index.ts index 513d948..d0f065a 100644 --- a/src/runner/cse-machine/src/index.ts +++ b/src/runner/cse-machine/src/index.ts @@ -5,7 +5,7 @@ import { type CseSnapshot, type CseSnapshotMessage, } from '@sourceacademy/common-cse-machine'; -import type { IPlugin, IChannel, IConduit } from '@sourceacademy/conductor/conduit'; +import type { IChannel, IConduit, IPlugin } from '@sourceacademy/conductor/conduit'; /** * Runner-side CSE machine plugin. diff --git a/src/runner/data-visualizer/rollup.config.mjs b/src/runner/data-visualizer/rollup.config.mjs index f9ac364..875363f 100644 --- a/src/runner/data-visualizer/rollup.config.mjs +++ b/src/runner/data-visualizer/rollup.config.mjs @@ -1,9 +1,9 @@ // @ts-check -import { defineConfig } from 'rollup'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/runner/data-visualizer/src/__tests__/runner.test.ts b/src/runner/data-visualizer/src/__tests__/runner.test.ts index abd03b8..a64b8f5 100644 --- a/src/runner/data-visualizer/src/__tests__/runner.test.ts +++ b/src/runner/data-visualizer/src/__tests__/runner.test.ts @@ -1,10 +1,10 @@ -import { expect, test } from 'vitest'; - import { DATA_VISUALIZER_CHANNEL_ID, RUNNER_ID, type SerializedDataVisualizerNode, } from '@sourceacademy/common-data-visualizer'; +import { expect, test } from 'vitest'; + import { BaseDataVisualizerRunnerPlugin, createRefIdAllocator, type RefIdAllocator } from '..'; class FakeChannel { diff --git a/src/runner/module-loader/rollup.config.mjs b/src/runner/module-loader/rollup.config.mjs index f9ac364..875363f 100644 --- a/src/runner/module-loader/rollup.config.mjs +++ b/src/runner/module-loader/rollup.config.mjs @@ -1,9 +1,9 @@ // @ts-check -import { defineConfig } from 'rollup'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/runner/module-loader/src/__tests__/runner.test.ts b/src/runner/module-loader/src/__tests__/runner.test.ts index ddb2355..45cde2b 100644 --- a/src/runner/module-loader/src/__tests__/runner.test.ts +++ b/src/runner/module-loader/src/__tests__/runner.test.ts @@ -1,15 +1,15 @@ -import { afterEach, describe, expect, test, vi, type Mock, type MockedFunction } from 'vitest'; -import { ModuleLoaderRunnerPlugin } from '..'; import { CHANNEL_ID, ModuleLoaderMessageType, RUNNER_ID, type ModuleLoaderMessage, } from '@sourceacademy/common-module-loader'; -import type { IChannel, IConduit } from '@sourceacademy/conductor/conduit'; +import type { IChannel, IConduit, PluginClass } from '@sourceacademy/conductor/conduit'; import type { IModulePlugin } from '@sourceacademy/conductor/module'; import type { IInterfacableEvaluator, IRunnerPlugin } from '@sourceacademy/conductor/runner'; -import type { PluginClass } from '@sourceacademy/conductor/conduit'; +import { afterEach, describe, expect, test, vi, type Mock, type MockedFunction } from 'vitest'; +import { ModuleLoaderRunnerPlugin } from '..'; + type ChannelSubscriber = (msg: ModuleLoaderMessage) => void | Promise; type TestChannel = IChannel & { diff --git a/src/runner/remoteExecution/index.ts b/src/runner/remoteExecution/index.ts index 011eeb5..271b1c3 100644 --- a/src/runner/remoteExecution/index.ts +++ b/src/runner/remoteExecution/index.ts @@ -1,5 +1,5 @@ import { CHANNEL_ID, RUNNER_ID, type PySlangMessage } from '@sourceacademy/common-test'; -import type { IPlugin, IChannel, IConduit } from '@sourceacademy/conductor/conduit'; +import type { IChannel, IConduit, IPlugin } from '@sourceacademy/conductor/conduit'; import { EV3Engine } from 'py-slang/src/engines/ev3/EV3Engine'; export class remoteRunnerPlugin implements IPlugin { diff --git a/src/runner/remoteExecution/rollup.config.mjs b/src/runner/remoteExecution/rollup.config.mjs index 5bb0583..dbba18b 100644 --- a/src/runner/remoteExecution/rollup.config.mjs +++ b/src/runner/remoteExecution/rollup.config.mjs @@ -1,9 +1,9 @@ // @ts-check -import { defineConfig } from 'rollup'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/runner/stepper/rollup.config.mjs b/src/runner/stepper/rollup.config.mjs index f9ac364..875363f 100644 --- a/src/runner/stepper/rollup.config.mjs +++ b/src/runner/stepper/rollup.config.mjs @@ -1,9 +1,9 @@ // @ts-check -import { defineConfig } from 'rollup'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/runner/stepper/src/__tests__/runner.test.ts b/src/runner/stepper/src/__tests__/runner.test.ts index a5a2171..d3cbcd3 100644 --- a/src/runner/stepper/src/__tests__/runner.test.ts +++ b/src/runner/stepper/src/__tests__/runner.test.ts @@ -1,6 +1,6 @@ +import { RUNNER_ID, STEPPER_CHANNEL_ID } from '@sourceacademy/common-stepper'; import { expect, test } from 'vitest'; -import { RUNNER_ID, STEPPER_CHANNEL_ID } from '@sourceacademy/common-stepper'; import { BaseStepperRunnerPlugin } from '..'; class FakeChannel { diff --git a/src/runner/test/rollup.config.mjs b/src/runner/test/rollup.config.mjs index f9ac364..875363f 100644 --- a/src/runner/test/rollup.config.mjs +++ b/src/runner/test/rollup.config.mjs @@ -1,9 +1,9 @@ // @ts-check -import { defineConfig } from 'rollup'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/runner/test/src/__tests__/runner.test.ts b/src/runner/test/src/__tests__/runner.test.ts index 1978e45..0ce876f 100644 --- a/src/runner/test/src/__tests__/runner.test.ts +++ b/src/runner/test/src/__tests__/runner.test.ts @@ -1,4 +1,7 @@ -import { vi, test, expect } from 'vitest'; +import type { PySlangMessage } from '@sourceacademy/common-test'; +import { remoteRunnerPlugin } from '@sourceacademy/runner-remote-execution'; +import { expect, test, vi } from 'vitest'; + vi.mock( import('py-slang/src/engines/ev3/EV3Engine'), @@ -12,9 +15,6 @@ vi.mock( }) as any, ); -import { remoteRunnerPlugin } from '@sourceacademy/runner-remote-execution'; -import type { PySlangMessage } from '@sourceacademy/common-test'; - test('plugin subscribes to channel and sends result on run message', async () => { const sentMessages: PySlangMessage[] = []; let messageHandler: (msg: PySlangMessage) => void = () => {}; diff --git a/src/web/autocomplete/rollup.config.mjs b/src/web/autocomplete/rollup.config.mjs index c81a80d..952d536 100644 --- a/src/web/autocomplete/rollup.config.mjs +++ b/src/web/autocomplete/rollup.config.mjs @@ -1,10 +1,10 @@ // @ts-check -import { defineConfig } from 'rollup'; +import commonjs from '@rollup/plugin-commonjs'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; -import commonjs from '@rollup/plugin-commonjs'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/web/autocomplete/src/index.ts b/src/web/autocomplete/src/index.ts index 6771070..871c91c 100644 --- a/src/web/autocomplete/src/index.ts +++ b/src/web/autocomplete/src/index.ts @@ -1,11 +1,3 @@ -import { - makeRpc, - type IChannel, - type IConduit, - type IPlugin, - type IRpcMessage, - type Remote, -} from '@sourceacademy/conductor/conduit'; import { AUTOCOMPLETE_CHANNEL_ID, SYNTAX_CHANNEL_ID, @@ -19,6 +11,14 @@ import { type TransferredModeFunction, type TransferredSyntaxHighlightData, } from '@sourceacademy/common-autocomplete'; +import { + makeRpc, + type IChannel, + type IConduit, + type IPlugin, + type IRpcMessage, + type Remote, +} from '@sourceacademy/conductor/conduit'; let nextAutoCompleteRequestId = 0; diff --git a/src/web/cse-machine/rollup.config.mjs b/src/web/cse-machine/rollup.config.mjs index f9ac364..875363f 100644 --- a/src/web/cse-machine/rollup.config.mjs +++ b/src/web/cse-machine/rollup.config.mjs @@ -1,9 +1,9 @@ // @ts-check -import { defineConfig } from 'rollup'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/web/cse-machine/src/__tests__/web.test.ts b/src/web/cse-machine/src/__tests__/web.test.ts index 71c7dba..085e5a4 100644 --- a/src/web/cse-machine/src/__tests__/web.test.ts +++ b/src/web/cse-machine/src/__tests__/web.test.ts @@ -1,5 +1,3 @@ -import { describe, test, expect, vi } from 'vitest'; -import { CseMachineHostPlugin } from '..'; import { CSE_CHANNEL, CSE_MESSAGE_TYPE_SNAPSHOTS, @@ -7,6 +5,8 @@ import { type CseSnapshot, } from '@sourceacademy/common-cse-machine'; import type { IChannel, IConduit } from '@sourceacademy/conductor/conduit'; +import { describe, expect, test, vi } from 'vitest'; +import { CseMachineHostPlugin } from '..'; const makeChannel = () => { let subscriber: ((msg: unknown) => void) | undefined; diff --git a/src/web/cse-machine/src/index.ts b/src/web/cse-machine/src/index.ts index 85c675d..c379e93 100644 --- a/src/web/cse-machine/src/index.ts +++ b/src/web/cse-machine/src/index.ts @@ -5,7 +5,7 @@ import { type CseSnapshot, type CseSnapshotMessage, } from '@sourceacademy/common-cse-machine'; -import type { IPlugin, IChannel, IConduit } from '@sourceacademy/conductor/conduit'; +import type { IChannel, IConduit, IPlugin } from '@sourceacademy/conductor/conduit'; // Re-export the protocol types so host apps can depend on a single package for the // web side of the CSE machine. diff --git a/src/web/data-visualizer/rollup.config.mjs b/src/web/data-visualizer/rollup.config.mjs index 7aa35c5..c0167d7 100644 --- a/src/web/data-visualizer/rollup.config.mjs +++ b/src/web/data-visualizer/rollup.config.mjs @@ -1,11 +1,11 @@ // @ts-check -import { defineConfig } from 'rollup'; import commonjs from '@rollup/plugin-commonjs'; import nodeResolve from '@rollup/plugin-node-resolve'; import replace from '@rollup/plugin-replace'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; // React, Blueprint, and Konva/react-konva are provided by the host at load time through the // require-provider (see the frontend's `requireProvider`, which already exposes them for other diff --git a/src/web/data-visualizer/src/DataVisualizerView.tsx b/src/web/data-visualizer/src/DataVisualizerView.tsx index 1d3618f..f8f11fb 100644 --- a/src/web/data-visualizer/src/DataVisualizerView.tsx +++ b/src/web/data-visualizer/src/DataVisualizerView.tsx @@ -1,7 +1,7 @@ import { Button, ButtonGroup, Card, Classes } from '@blueprintjs/core'; +import type { SerializedDataVisualizerRow } from '@sourceacademy/common-data-visualizer'; import { useEffect, useState } from 'react'; -import type { SerializedDataVisualizerRow } from '@sourceacademy/common-data-visualizer'; import { Config } from './Config'; import { Tree, type ViewMode } from './tree/Tree'; diff --git a/src/web/data-visualizer/src/__tests__/DataVisualizerView.test.tsx b/src/web/data-visualizer/src/__tests__/DataVisualizerView.test.tsx index 65b14ce..a95d205 100644 --- a/src/web/data-visualizer/src/__tests__/DataVisualizerView.test.tsx +++ b/src/web/data-visualizer/src/__tests__/DataVisualizerView.test.tsx @@ -1,8 +1,13 @@ +import { Button } from '@blueprintjs/core'; import type { SerializedDataVisualizerRow } from '@sourceacademy/common-data-visualizer'; import { act } from 'react'; import TestRenderer from 'react-test-renderer'; import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; + +import DataVisualizerView from '../DataVisualizerView'; +import { Tree } from '../tree/Tree'; + // Same rationale as the other __tests__ files: avoid Konva's Node-environment build requiring the // optional `canvas` package. DataVisualizerView pulls this in transitively via Tree/the drawers. vi.mock( @@ -40,11 +45,6 @@ vi.mock(import('react-konva'), () => { } as any; }); -import { Button } from '@blueprintjs/core'; - -import DataVisualizerView from '../DataVisualizerView'; -import { Tree } from '../tree/Tree'; - // React 19 warns ("The current testing environment is not configured to support act(...)") // unless this is set - the standard flag for a hand-rolled (non testing-library) React test setup. (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; @@ -112,7 +112,7 @@ const renderView = (rows: SerializedDataVisualizerRow[]): TestRenderer.ReactTest return renderer; }; -describe('DataVisualizerView', () => { +describe(DataVisualizerView, () => { test('shows the placeholder help text and no controls when there are no rows yet', () => { const renderer = renderView([]); expect(renderer.root.findByProps({ id: 'data-visualizer-default-text' })).toBeTruthy(); diff --git a/src/web/data-visualizer/src/__tests__/classify.test.ts b/src/web/data-visualizer/src/__tests__/classify.test.ts index 69fdfce..59290aa 100644 --- a/src/web/data-visualizer/src/__tests__/classify.test.ts +++ b/src/web/data-visualizer/src/__tests__/classify.test.ts @@ -1,6 +1,6 @@ +import type { SerializedDataVisualizerNode } from '@sourceacademy/common-data-visualizer'; import { expect, test } from 'vitest'; -import type { SerializedDataVisualizerNode } from '@sourceacademy/common-data-visualizer'; import { classify } from '../classify'; let nextRefId = 1; diff --git a/src/web/data-visualizer/src/__tests__/tree.test.ts b/src/web/data-visualizer/src/__tests__/tree.test.ts index 10c5521..1df05da 100644 --- a/src/web/data-visualizer/src/__tests__/tree.test.ts +++ b/src/web/data-visualizer/src/__tests__/tree.test.ts @@ -1,6 +1,17 @@ import type { SerializedDataVisualizerNode } from '@sourceacademy/common-data-visualizer'; +import { Group, Line, Rect, Text } from 'react-konva'; import { describe, expect, test, vi } from 'vitest'; + +import { Config } from '../Config'; +import ArrayDrawable from '../drawable/ArrayDrawable'; +import { NullDrawable } from '../drawable/Drawable'; +import { formatLeaf } from '../format'; +import { AlreadyParsedTreeNode } from '../tree/AlreadyParsedTreeNode'; +import { ClassicDrawer } from '../tree/ClassicDrawer'; +import { Tree } from '../tree/Tree'; +import { ArrayTreeNode, DataTreeNode, FunctionTreeNode, TreeNode } from '../tree/TreeNode'; + // These tests exercise layout arithmetic and presentational branching, never actually rendering // to a canvas — but importing the real `konva`/`react-konva` still runs their module-init code, // which detects vitest's Node environment (no `window`) and switches to Konva's server-side @@ -44,17 +55,6 @@ vi.mock(import('react-konva'), () => { } as any; }); -import { Group, Line, Rect, Text } from 'react-konva'; - -import { Config } from '../Config'; -import { NullDrawable } from '../drawable/Drawable'; -import ArrayDrawable from '../drawable/ArrayDrawable'; -import { formatLeaf } from '../format'; -import { AlreadyParsedTreeNode } from '../tree/AlreadyParsedTreeNode'; -import { ClassicDrawer } from '../tree/ClassicDrawer'; -import { Tree } from '../tree/Tree'; -import { ArrayTreeNode, DataTreeNode, FunctionTreeNode, TreeNode } from '../tree/TreeNode'; - const leaf = (displayValue: string): SerializedDataVisualizerNode => ({ type: 'leaf', displayValue, @@ -325,7 +325,7 @@ describe('ClassicDrawer pixel math', () => { }); }); -describe('formatLeaf', () => { +describe(formatLeaf, () => { test('wraps and passes through short strings unchanged apart from quoting', () => { expect(formatLeaf('hi', 'string')).toBe('"hi"'); }); @@ -355,7 +355,7 @@ type ArrayDrawableOutput = React.ReactElement<{ ]; }>; -describe('ArrayDrawable', () => { +describe(ArrayDrawable, () => { const render = (nodes: DataTreeNode[]): ArrayDrawableOutput => new ArrayDrawable({ nodes, x: 0, y: 0, color: 'black' }).render() as ArrayDrawableOutput; diff --git a/src/web/data-visualizer/src/__tests__/treeViews.test.ts b/src/web/data-visualizer/src/__tests__/treeViews.test.ts index 240892e..6c1253d 100644 --- a/src/web/data-visualizer/src/__tests__/treeViews.test.ts +++ b/src/web/data-visualizer/src/__tests__/treeViews.test.ts @@ -1,6 +1,16 @@ import type { SerializedDataVisualizerNode } from '@sourceacademy/common-data-visualizer'; +import { Text } from 'react-konva'; import { describe, expect, test, vi } from 'vitest'; + +import type { ClassificationResult } from '../classify'; +import { ArrowDrawable, BackwardArrowDrawable } from '../drawable/Drawable'; +import { AlreadyParsedTreeNode } from '../tree/AlreadyParsedTreeNode'; +import { BinaryTreeDrawer } from '../tree/BinaryTreeDrawer'; +import { GeneralTreeDrawer } from '../tree/GeneralTreeDrawer'; +import { Tree } from '../tree/Tree'; +import { ArrayTreeNode, FunctionTreeNode } from '../tree/TreeNode'; + // Same rationale as tree.test.ts: avoid Konva's Node-environment build requiring the optional // `canvas` package for what are, here, pure layout-arithmetic and warning-fallback checks. vi.mock( @@ -38,16 +48,6 @@ vi.mock(import('react-konva'), () => { } as any; }); -import { Text } from 'react-konva'; - -import type { ClassificationResult } from '../classify'; -import { ArrowDrawable, BackwardArrowDrawable } from '../drawable/Drawable'; -import { AlreadyParsedTreeNode } from '../tree/AlreadyParsedTreeNode'; -import { BinaryTreeDrawer } from '../tree/BinaryTreeDrawer'; -import { GeneralTreeDrawer } from '../tree/GeneralTreeDrawer'; -import { Tree } from '../tree/Tree'; -import { ArrayTreeNode, FunctionTreeNode } from '../tree/TreeNode'; - const empty = (): SerializedDataVisualizerNode => ({ type: 'empty' }); const leaf = (n: number): SerializedDataVisualizerNode => ({ type: 'leaf', @@ -112,7 +112,7 @@ type DrawerInternals = { const internals = (drawer: BinaryTreeDrawer | GeneralTreeDrawer): DrawerInternals => drawer as DrawerInternals; -describe('BinaryTreeDrawer', () => { +describe(BinaryTreeDrawer, () => { test('a non-binary-tree structure draws the fixed-size warning box instead of a tree', () => { const tree = Tree.fromSerializedNode(pair(leaf(1), leaf(2))); const drawer = tree.draw('binaryTree') as BinaryTreeDrawer; @@ -214,7 +214,7 @@ describe('BinaryTreeDrawer', () => { }); }); -describe('GeneralTreeDrawer', () => { +describe(GeneralTreeDrawer, () => { test('a non-general-tree structure draws the fixed-size warning box instead of a tree', () => { // An *improper* list (here, a bare 2-tuple whose second slot is a leaf rather than another pair // or the empty terminator) is the only shape General Tree View actually rejects — a binary-tree- diff --git a/src/web/data-visualizer/src/tree/BinaryTreeDrawer.tsx b/src/web/data-visualizer/src/tree/BinaryTreeDrawer.tsx index 2c8920c..57a751a 100644 --- a/src/web/data-visualizer/src/tree/BinaryTreeDrawer.tsx +++ b/src/web/data-visualizer/src/tree/BinaryTreeDrawer.tsx @@ -2,9 +2,9 @@ import Konva from 'konva'; import { Layer, Stage, Text } from 'react-konva'; import { Config } from '../Config'; -import { formatLeaf } from '../format'; import type { ClassificationResult } from '../classify'; import { ArrowDrawable, BackwardArrowDrawable } from '../drawable/Drawable'; +import { formatLeaf } from '../format'; import { AlreadyParsedTreeNode } from './AlreadyParsedTreeNode'; import { ClassicDrawer } from './ClassicDrawer'; import type { Tree } from './Tree'; diff --git a/src/web/data-visualizer/src/tree/ClassicDrawer.tsx b/src/web/data-visualizer/src/tree/ClassicDrawer.tsx index ec54b1c..80edd96 100644 --- a/src/web/data-visualizer/src/tree/ClassicDrawer.tsx +++ b/src/web/data-visualizer/src/tree/ClassicDrawer.tsx @@ -2,8 +2,8 @@ import Konva from 'konva'; import { Layer, Stage, Text } from 'react-konva'; import { Config } from '../Config'; -import { formatLeaf } from '../format'; import { ArrowDrawable, BackwardArrowDrawable } from '../drawable/Drawable'; +import { formatLeaf } from '../format'; import { AlreadyParsedTreeNode } from './AlreadyParsedTreeNode'; import type { Tree } from './Tree'; import { diff --git a/src/web/data-visualizer/src/tree/GeneralTreeDrawer.tsx b/src/web/data-visualizer/src/tree/GeneralTreeDrawer.tsx index 297c219..f681542 100644 --- a/src/web/data-visualizer/src/tree/GeneralTreeDrawer.tsx +++ b/src/web/data-visualizer/src/tree/GeneralTreeDrawer.tsx @@ -2,9 +2,9 @@ import Konva from 'konva'; import { Layer, Stage, Text } from 'react-konva'; import { Config } from '../Config'; -import { formatLeaf } from '../format'; import type { ClassificationResult } from '../classify'; import { ArrowDrawable, BackwardArrowDrawable } from '../drawable/Drawable'; +import { formatLeaf } from '../format'; import { AlreadyParsedTreeNode } from './AlreadyParsedTreeNode'; import { ClassicDrawer } from './ClassicDrawer'; import type { Tree } from './Tree'; diff --git a/src/web/module-loader/rollup.config.mjs b/src/web/module-loader/rollup.config.mjs index c3808d7..7d85dc7 100644 --- a/src/web/module-loader/rollup.config.mjs +++ b/src/web/module-loader/rollup.config.mjs @@ -1,10 +1,10 @@ // @ts-check -import { defineConfig } from 'rollup'; +import commonjs from '@rollup/plugin-commonjs'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; -import commonjs from '@rollup/plugin-commonjs'; +import { defineConfig } from 'rollup'; export default defineConfig({ input: 'src/index.ts', diff --git a/src/web/module-loader/src/__tests__/web.test.ts b/src/web/module-loader/src/__tests__/web.test.ts index 661f5f8..d828f1c 100644 --- a/src/web/module-loader/src/__tests__/web.test.ts +++ b/src/web/module-loader/src/__tests__/web.test.ts @@ -1,5 +1,3 @@ -import { afterEach, describe, expect, test, vi, type Mock } from 'vitest'; -import { ModuleLoaderWebPlugin } from '..'; import { CHANNEL_ID, ModuleLoaderMessageType, @@ -7,6 +5,8 @@ import { type ModuleLoaderMessage, } from '@sourceacademy/common-module-loader'; import type { IChannel, IConduit } from '@sourceacademy/conductor/conduit'; +import { afterEach, describe, expect, test, vi, type Mock } from 'vitest'; +import { ModuleLoaderWebPlugin } from '..'; type ModuleDirectory = Record; type ChannelSubscriber = (msg: ModuleLoaderMessage) => void; diff --git a/src/web/module-loader/src/index.ts b/src/web/module-loader/src/index.ts index 0ab1b4e..fca5fce 100644 --- a/src/web/module-loader/src/index.ts +++ b/src/web/module-loader/src/index.ts @@ -5,10 +5,10 @@ import { type ModuleLoaderMessage, } from '@sourceacademy/common-module-loader'; import { - type IPlugin, + checkIsPluginClass, type IChannel, type IConduit, - checkIsPluginClass, + type IPlugin, } from '@sourceacademy/conductor/conduit'; type ModuleDirectoryBundle = { diff --git a/src/web/stepper/rollup.config.mjs b/src/web/stepper/rollup.config.mjs index a9e96af..c8bb240 100644 --- a/src/web/stepper/rollup.config.mjs +++ b/src/web/stepper/rollup.config.mjs @@ -1,10 +1,10 @@ // @ts-check -import { defineConfig } from 'rollup'; import commonjs from '@rollup/plugin-commonjs'; import nodeResolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; // React and Blueprint are provided by the host at load time through the require-provider (see the // frontend's `requireProvider`), so they are kept external and resolved via `require(...)` calls in diff --git a/src/web/stepper/src/SubstVisualizer.tsx b/src/web/stepper/src/SubstVisualizer.tsx index 14d29ad..476f896 100644 --- a/src/web/stepper/src/SubstVisualizer.tsx +++ b/src/web/stepper/src/SubstVisualizer.tsx @@ -388,7 +388,7 @@ function ProfileFunctionDefinitionPopover({ */ function ProfileHoverTextPopover({ text }: { text: string }) { return ( -
+
{` ${text}`} @@ -851,21 +851,21 @@ function renderNode( {part.parts.map((p, i) => renderPart(p, i))} ) : null; } - if ("unless" in part) { + if ('unless' in part) { return node[part.unless] ? null : ( {part.parts.map((p, i) => renderPart(p, i))} ); } - if ("image" in part) { + if ('image' in part) { const src = readNodeProp(node, part.image); - if (typeof src !== "string" || !src.startsWith("data:")) return null; + if (typeof src !== 'string' || !src.startsWith('data:')) return null; const alt = part.altProp === undefined ? undefined : readNodeProp(node, part.altProp); return ( {alt ); @@ -965,7 +965,7 @@ function renderNode( // just whichever branch happened to run first. See SyntaxProfile.hoverText. const hoverRule = profile.hoverText?.find(rule => rule.type === currentNode.type); const hoverText = hoverRule ? readNodeProp(currentNode, hoverRule.textProp) : undefined; - if (typeof hoverText === "string" && hoverText !== "") { + if (typeof hoverText === 'string' && hoverText !== '') { const content = result; result = ( Date: Mon, 17 Aug 2026 10:06:09 +0800 Subject: [PATCH 14/29] Properly merge --- src/web/stepper/src/SubstVisualizer.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/web/stepper/src/SubstVisualizer.tsx b/src/web/stepper/src/SubstVisualizer.tsx index 7e053eb..348e592 100644 --- a/src/web/stepper/src/SubstVisualizer.tsx +++ b/src/web/stepper/src/SubstVisualizer.tsx @@ -881,13 +881,6 @@ function renderNode( return ( -======= interactionKind="hover" placement="bottom" usePortal={popoverDepth === 0} @@ -901,7 +894,6 @@ function renderNode( alt={altText} /> ->>>>>>> main ); } return null; From 76110c4430377ab4e76355089884b749da1841e4 Mon Sep 17 00:00:00 2001 From: "LAPTOP-1A6BH9TL\\Lee Yi" Date: Mon, 17 Aug 2026 13:37:13 +0800 Subject: [PATCH 15/29] Fix dependencies and devDependencies not being specified correctly for web-stepper --- package.json | 1 + src/web/stepper/package.json | 21 ++++++++++++--------- yarn.lock | 20 ++++++++++++++++++++ 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 5161f34..5c7332e 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ }, "devDependencies": { "@changesets/cli": "^2.31.0", + "@commander-js/extra-typings": "^15.0.0", "@sourceacademy/conductor": "catalog:", "@sourceacademy/plugin-directory": "source-academy/plugin-directory#main", "@types/node": "^25.9.1", diff --git a/src/web/stepper/package.json b/src/web/stepper/package.json index a06ba11..ba6fe64 100644 --- a/src/web/stepper/package.json +++ b/src/web/stepper/package.json @@ -1,6 +1,6 @@ { "name": "@sourceacademy/web-stepper", - "version": "0.0.1", + "version": "0.0.2", "packageManager": "yarn@4.18.0", "description": "Host (web) plugin for the Source Academy stepper: renders evaluation steps", "repository": { @@ -29,22 +29,25 @@ "react-dom": ">=18" }, "devDependencies": { - "@blueprintjs/core": "^6.0.0", - "@blueprintjs/icons": "^6.0.0", - "@mantine/hooks": "^9.0.0", "@rollup/plugin-commonjs": "catalog:", "@rollup/plugin-node-resolve": "catalog:", "@rollup/plugin-terser": "catalog:", "@rollup/plugin-typescript": "catalog:", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:" + }, + "dependencies": { + "@blueprintjs/core": "^6.0.0", + "@blueprintjs/icons": "^6.0.0", + "@mantine/hooks": "^9.0.0", "@sourceacademy/common-stepper": "workspace:*", "@sourceacademy/common-tabs": "workspace:^", "@sourceacademy/conductor": "catalog:", - "@types/react": "^19.0.0", "classnames": "^2.3.2", "react": "^19.0.0", - "react-dom": "^19.0.0", - "rollup": "catalog:", - "tslib": "catalog:", - "typescript": "catalog:" + "react-dom": "^19.0.0" } } diff --git a/yarn.lock b/yarn.lock index b2a847a..7f512c6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -474,6 +474,15 @@ __metadata: languageName: node linkType: hard +"@commander-js/extra-typings@npm:^15.0.0": + version: 15.0.0 + resolution: "@commander-js/extra-typings@npm:15.0.0" + peerDependencies: + commander: ~15.0.0 + checksum: 10c0/4282857c0acb2da1b893fe8c39a939bdfae2d7cbb43a22c1fe8ef5bef4cf615ddb34d96239f7603294ef5014728799c8212f4451fa9d2e469cce087eb89f0218 + languageName: node + linkType: hard + "@emnapi/core@npm:1.10.0": version: 1.10.0 resolution: "@emnapi/core@npm:1.10.0" @@ -1355,6 +1364,7 @@ __metadata: resolution: "@sourceacademy/plugins-workspace@workspace:." dependencies: "@changesets/cli": "npm:^2.31.0" + "@commander-js/extra-typings": "npm:^15.0.0" "@sourceacademy/conductor": "catalog:" "@sourceacademy/plugin-directory": "source-academy/plugin-directory#main" "@types/node": "npm:^25.9.1" @@ -1622,6 +1632,7 @@ __metadata: "@sourceacademy/common-tabs": "workspace:^" "@sourceacademy/conductor": "catalog:" "@types/react": "npm:^19.0.0" + "@types/react-dom": "npm:^19.0.0" classnames: "npm:^2.3.2" react: "npm:^19.0.0" react-dom: "npm:^19.0.0" @@ -1736,6 +1747,15 @@ __metadata: languageName: node linkType: hard +"@types/react-dom@npm:^19.0.0": + version: 19.2.4 + resolution: "@types/react-dom@npm:19.2.4" + peerDependencies: + "@types/react": ^19.2.0 + checksum: 10c0/b7d854ce17bb51a3a067168268a90f0123d10dc490f63f1ff5409f07ac715febeb8f5b7b473404a9d437106571e0312fc2937a945634d590abfc9aa46a14b01b + languageName: node + linkType: hard + "@types/react-reconciler@npm:^0.28.9": version: 0.28.9 resolution: "@types/react-reconciler@npm:0.28.9" From a5f760a177acac493c6bc894c54ab1c4d15dc79f Mon Sep 17 00:00:00 2001 From: "LAPTOP-1A6BH9TL\\Lee Yi" Date: Mon, 17 Aug 2026 13:37:21 +0800 Subject: [PATCH 16/29] Add a clean command --- eslint.config.mjs | 10 +++ lib/build.ts | 107 ++++++++++++++++++------------ src/web/stepper/rollup.config.mjs | 5 ++ 3 files changed, 79 insertions(+), 43 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index b470246..497e218 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -34,6 +34,16 @@ export default defineConfig([ }, } ], + + 'no-restricted-imports': [ + 'error', + { + paths: [{ + name: 'commander', + message: 'Use @commander-js/extra-typings instead!' + }] + } + ] } }, tseslint.configs.recommended, diff --git a/lib/build.ts b/lib/build.ts index 41b65a3..c006c9f 100644 --- a/lib/build.ts +++ b/lib/build.ts @@ -3,7 +3,7 @@ import fs from 'fs/promises'; import { createRequire } from 'module'; import pathlib from 'path'; import type { IPluginDefinition } from '@sourceacademy/plugin-directory/dist/types/IPluginDefinition.js'; -import { Command } from 'commander'; +import { Command } from '@commander-js/extra-typings'; import packageJSON from '../package.json' with { type: 'json' }; interface WorkspaceEntry { @@ -44,8 +44,9 @@ const pluginDirectory: Record = {}; * Generates the manifest files for each plugin and updates the changeset config to ignore non-installable plugins. * The manifest files are generated by reading the manifest.json file in each plugin folder and writing it to the dist folder. * The changeset config is updated to ignore non-installable plugins to prevent unnecessary version bumps and releases. - * @example + * @example ``` * await generateManifest(); + * ``` * This will generate the manifest files for each plugin and update the changeset config to ignore non-installable plugins. */ export async function generateManifest() { @@ -137,25 +138,27 @@ export async function generateManifest() { } /** - * A helper function to spawn a child process and return a promise that resolves when the process exits with code 0, and rejects otherwise. - * @param args The arguments passed to spawn - * @returns A promise that resolves when the process exits with code 0, and rejects otherwise. - * @example - * await spawnPromise("yarn", ["build"]); + * Runs `yarn` with the given arguments. */ -function spawnPromise(...args: Parameters) { +function runYarnUsingSpawn(...args: string[]) { return new Promise((resolve, reject) => { - const child = spawn(...args); - child.on('error', err => { - reject(err); - }); - child.on('close', code => { + const proc = spawn( + process.platform === 'win32' ? 'yarn.cmd' : 'yarn', + args, + { + stdio: 'inherit', + shell: true, + cwd: repoRoot, + }, + ); + proc.on('error', reject); + proc.on('close', code => { if (code === 0) { resolve(); } else { reject(new Error(`Process exited with code ${code}`)); } - }); + }) }); } @@ -185,29 +188,6 @@ async function copyDistFiles(globalManifest: Record { + await build(cmd.args); + }), + { isDefault: true } + ) + .addCommand( + new Command('clean') + .description('Remove dist folders') + .action(clean) + ) + // Only run automatically if not testing if (process.env.NODE_ENV !== 'test') { - const program = new Command(); - - // TODO: Add more options - const options = program.parse(); - const extraArgs = options.args; - await build(extraArgs); + await program.parseAsync(); } diff --git a/src/web/stepper/rollup.config.mjs b/src/web/stepper/rollup.config.mjs index c8bb240..a4d0740 100644 --- a/src/web/stepper/rollup.config.mjs +++ b/src/web/stepper/rollup.config.mjs @@ -32,4 +32,9 @@ export default defineConfig({ typescript({ exclude: ['**/__tests__/**'] }), terser(), ], + onwarn(warning, warn) { + // Suppress the warnings about 'use client' from @mantine + if (warning.code === 'MODULE_LEVEL_DIRECTIVE') return; + warn(warning); + } }); From d11f8c82640bc4dc6a91e48f314694526be3f215 Mon Sep 17 00:00:00 2001 From: "LAPTOP-1A6BH9TL\\Lee Yi" Date: Mon, 17 Aug 2026 13:40:55 +0800 Subject: [PATCH 17/29] Run format --- .github/workflows/on_pr.yml | 1 + eslint.config.mjs | 24 ++++++------ lib/build.ts | 37 ++++++++++--------- src/runner/autocomplete/src/index.ts | 1 - .../src/__tests__/runner.test.ts | 2 +- src/runner/test/src/__tests__/runner.test.ts | 1 - .../src/__tests__/DataVisualizerView.test.tsx | 1 - .../src/__tests__/tree.test.ts | 1 - .../src/__tests__/treeViews.test.ts | 1 - src/web/stepper/rollup.config.mjs | 2 +- src/web/stepper/src/SubstVisualizer.tsx | 8 ++-- 11 files changed, 39 insertions(+), 40 deletions(-) diff --git a/.github/workflows/on_pr.yml b/.github/workflows/on_pr.yml index 268e800..26b8390 100644 --- a/.github/workflows/on_pr.yml +++ b/.github/workflows/on_pr.yml @@ -18,6 +18,7 @@ jobs: node-version: 24 cache: yarn - run: yarn install --frozen-lockfile + - run: yarn constraints - run: yarn run build - run: yarn run format:ci - run: yarn run lint diff --git a/eslint.config.mjs b/eslint.config.mjs index 497e218..bc0b335 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -13,7 +13,7 @@ export default defineConfig([ eslintConfigPrettierFlat, { plugins: { - import: importPlugin + import: importPlugin, }, rules: { 'import/first': 'warn', @@ -26,25 +26,27 @@ export default defineConfig([ groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'], named: { import: true, - types: 'types-last' + types: 'types-last', }, alphabetize: { order: 'asc', - orderImportKind: 'asc' + orderImportKind: 'asc', }, - } + }, ], 'no-restricted-imports': [ 'error', { - paths: [{ - name: 'commander', - message: 'Use @commander-js/extra-typings instead!' - }] - } - ] - } + paths: [ + { + name: 'commander', + message: 'Use @commander-js/extra-typings instead!', + }, + ], + }, + ], + }, }, tseslint.configs.recommended, { diff --git a/lib/build.ts b/lib/build.ts index c006c9f..e6c28eb 100644 --- a/lib/build.ts +++ b/lib/build.ts @@ -142,15 +142,11 @@ export async function generateManifest() { */ function runYarnUsingSpawn(...args: string[]) { return new Promise((resolve, reject) => { - const proc = spawn( - process.platform === 'win32' ? 'yarn.cmd' : 'yarn', - args, - { - stdio: 'inherit', - shell: true, - cwd: repoRoot, - }, - ); + const proc = spawn(process.platform === 'win32' ? 'yarn.cmd' : 'yarn', args, { + stdio: 'inherit', + shell: true, + cwd: repoRoot, + }); proc.on('error', reject); proc.on('close', code => { if (code === 0) { @@ -158,7 +154,7 @@ function runYarnUsingSpawn(...args: string[]) { } else { reject(new Error(`Process exited with code ${code}`)); } - }) + }); }); } @@ -188,7 +184,6 @@ async function copyDistFiles(globalManifest: Record { await build(cmd.args); }), - { isDefault: true } - ) - .addCommand( - new Command('clean') - .description('Remove dist folders') - .action(clean) + { isDefault: true }, ) + .addCommand(new Command('clean').description('Remove dist folders').action(clean)); // Only run automatically if not testing if (process.env.NODE_ENV !== 'test') { diff --git a/src/runner/autocomplete/src/index.ts b/src/runner/autocomplete/src/index.ts index 4b96260..2a30d9e 100644 --- a/src/runner/autocomplete/src/index.ts +++ b/src/runner/autocomplete/src/index.ts @@ -19,7 +19,6 @@ import { type IRpcMessage, } from '@sourceacademy/conductor/conduit'; - const transferModeFunction = (name: keyof ModeRpc, fn: ModeFunction): TransferredModeFunction => typeof fn === 'function' ? { rpc: name } : fn; diff --git a/src/runner/module-loader/src/__tests__/runner.test.ts b/src/runner/module-loader/src/__tests__/runner.test.ts index 45cde2b..a52a03c 100644 --- a/src/runner/module-loader/src/__tests__/runner.test.ts +++ b/src/runner/module-loader/src/__tests__/runner.test.ts @@ -4,7 +4,7 @@ import { RUNNER_ID, type ModuleLoaderMessage, } from '@sourceacademy/common-module-loader'; -import type { IChannel, IConduit, PluginClass } from '@sourceacademy/conductor/conduit'; +import type { IChannel, IConduit, PluginClass } from '@sourceacademy/conductor/conduit'; import type { IModulePlugin } from '@sourceacademy/conductor/module'; import type { IInterfacableEvaluator, IRunnerPlugin } from '@sourceacademy/conductor/runner'; import { afterEach, describe, expect, test, vi, type Mock, type MockedFunction } from 'vitest'; diff --git a/src/runner/test/src/__tests__/runner.test.ts b/src/runner/test/src/__tests__/runner.test.ts index 0ce876f..062ad00 100644 --- a/src/runner/test/src/__tests__/runner.test.ts +++ b/src/runner/test/src/__tests__/runner.test.ts @@ -2,7 +2,6 @@ import type { PySlangMessage } from '@sourceacademy/common-test'; import { remoteRunnerPlugin } from '@sourceacademy/runner-remote-execution'; import { expect, test, vi } from 'vitest'; - vi.mock( import('py-slang/src/engines/ev3/EV3Engine'), () => diff --git a/src/web/data-visualizer/src/__tests__/DataVisualizerView.test.tsx b/src/web/data-visualizer/src/__tests__/DataVisualizerView.test.tsx index a95d205..0b6bd7b 100644 --- a/src/web/data-visualizer/src/__tests__/DataVisualizerView.test.tsx +++ b/src/web/data-visualizer/src/__tests__/DataVisualizerView.test.tsx @@ -4,7 +4,6 @@ import { act } from 'react'; import TestRenderer from 'react-test-renderer'; import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; - import DataVisualizerView from '../DataVisualizerView'; import { Tree } from '../tree/Tree'; diff --git a/src/web/data-visualizer/src/__tests__/tree.test.ts b/src/web/data-visualizer/src/__tests__/tree.test.ts index 1df05da..b20cae2 100644 --- a/src/web/data-visualizer/src/__tests__/tree.test.ts +++ b/src/web/data-visualizer/src/__tests__/tree.test.ts @@ -2,7 +2,6 @@ import type { SerializedDataVisualizerNode } from '@sourceacademy/common-data-vi import { Group, Line, Rect, Text } from 'react-konva'; import { describe, expect, test, vi } from 'vitest'; - import { Config } from '../Config'; import ArrayDrawable from '../drawable/ArrayDrawable'; import { NullDrawable } from '../drawable/Drawable'; diff --git a/src/web/data-visualizer/src/__tests__/treeViews.test.ts b/src/web/data-visualizer/src/__tests__/treeViews.test.ts index 6c1253d..301fa57 100644 --- a/src/web/data-visualizer/src/__tests__/treeViews.test.ts +++ b/src/web/data-visualizer/src/__tests__/treeViews.test.ts @@ -2,7 +2,6 @@ import type { SerializedDataVisualizerNode } from '@sourceacademy/common-data-vi import { Text } from 'react-konva'; import { describe, expect, test, vi } from 'vitest'; - import type { ClassificationResult } from '../classify'; import { ArrowDrawable, BackwardArrowDrawable } from '../drawable/Drawable'; import { AlreadyParsedTreeNode } from '../tree/AlreadyParsedTreeNode'; diff --git a/src/web/stepper/rollup.config.mjs b/src/web/stepper/rollup.config.mjs index a4d0740..cd4af12 100644 --- a/src/web/stepper/rollup.config.mjs +++ b/src/web/stepper/rollup.config.mjs @@ -36,5 +36,5 @@ export default defineConfig({ // Suppress the warnings about 'use client' from @mantine if (warning.code === 'MODULE_LEVEL_DIRECTIVE') return; warn(warning); - } + }, }); diff --git a/src/web/stepper/src/SubstVisualizer.tsx b/src/web/stepper/src/SubstVisualizer.tsx index 348e592..ae2ffe8 100644 --- a/src/web/stepper/src/SubstVisualizer.tsx +++ b/src/web/stepper/src/SubstVisualizer.tsx @@ -400,7 +400,7 @@ function ProfileHoverTextPopover({ text }: { text: string }) { /** Popover body for an `image` part: the inline opaque-value thumbnail, enlarged to be legible. */ function EnlargedThumbnailPopover({ src, alt }: { src: string; alt?: string }) { return ( -
+
{alt ? ( <> @@ -408,7 +408,7 @@ function EnlargedThumbnailPopover({ src, alt }: { src: string; alt?: string }) { {` ${alt}`} ) : null} - {alt + {alt
); @@ -877,7 +877,7 @@ function renderNode( const src = readNodeProp(node, part.image); if (typeof src !== 'string' || !src.startsWith('data:')) return null; const alt = part.altProp === undefined ? undefined : readNodeProp(node, part.altProp); - const altText = alt == null ? "" : String(alt); + const altText = alt == null ? '' : String(alt); return ( } > {altText} From 391c2f727d93b4bbe5178ebe12d9a2b43284fa46 Mon Sep 17 00:00:00 2001 From: "LAPTOP-1A6BH9TL\\Lee Yi" Date: Mon, 17 Aug 2026 13:49:53 +0800 Subject: [PATCH 18/29] Fix broken tests --- src/common/cse-machine/src/__tests__/common.test.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/common/cse-machine/src/__tests__/common.test.ts b/src/common/cse-machine/src/__tests__/common.test.ts index 4a4bd29..63732a1 100644 --- a/src/common/cse-machine/src/__tests__/common.test.ts +++ b/src/common/cse-machine/src/__tests__/common.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable vitest/prefer-describe-function-title */ import { describe, expect, it } from 'vitest'; import { CSE_CHANNEL, @@ -96,7 +97,7 @@ describe('structured-clone safety', () => { // ── CseSnapshotMessage ──────────────────────────────────────────────────────── -describe(CseSnapshotMessage, () => { +describe('CseSnapshotMessage', () => { it('totalSteps matches snapshots.length for a batch', () => { const snapshots: CseSnapshot[] = [ { stepIndex: 0, control: [], stash: [], environments: [] }, @@ -198,7 +199,7 @@ describe('CseSnapshot optional fields', () => { // ── CseSerializedValue ──────────────────────────────────────────────────────── -describe(CseSerializedValue, () => { +describe('CseSerializedValue', () => { it('accepts minimal value with displayValue and label only', () => { const v: CseSerializedValue = { displayValue: 'true', label: 'bool' }; expect(v.tag).toBeUndefined(); @@ -219,7 +220,7 @@ describe(CseSerializedValue, () => { // ── CseSerializedInstruction ────────────────────────────────────────────────── -describe(CseSerializedInstruction, () => { +describe('CseSerializedInstruction', () => { it('accepts minimal instruction with displayText only', () => { const instr: CseSerializedInstruction = { displayText: 'pop' }; expect(instr.tag).toBeUndefined(); @@ -242,7 +243,7 @@ describe(CseSerializedInstruction, () => { // ── CseSerializedEnvFrame ───────────────────────────────────────────────────── -describe(CseSerializedEnvFrame, () => { +describe('CseSerializedEnvFrame', () => { it('root frame has null parentId', () => { const frame: CseSerializedEnvFrame = { id: 'g', @@ -318,7 +319,7 @@ describe(CseSerializedEnvFrame, () => { // ── CseSerializedBinding ────────────────────────────────────────────────────── -describe(CseSerializedBinding, () => { +describe('CseSerializedBinding', () => { it('isConst is optional and defaults to undefined', () => { const b: CseSerializedBinding = { name: 'x', From 23457136642533797241a8d27a1c715ecd33693d Mon Sep 17 00:00:00 2001 From: "LAPTOP-1A6BH9TL\\Lee Yi" Date: Mon, 17 Aug 2026 15:49:36 +0800 Subject: [PATCH 19/29] Add tests for build script --- .vscode/settings.json | 8 +- eslint.config.mjs | 1 + lib/__tests__/build.test.ts | 141 ++++++++++++++++++++++++++++++++++++ lib/build.ts | 38 +++++----- lib/manifest.schema.json | 11 +++ 5 files changed, 179 insertions(+), 20 deletions(-) create mode 100644 lib/__tests__/build.test.ts create mode 100644 lib/manifest.schema.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 91f8972..7d19b92 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,5 +6,11 @@ "js/ts.tsdk.promptToUseWorkspaceVersion": true, "js/ts.tsdk.path": ".yarn/sdks/typescript/lib", "eslint.nodePath": ".yarn/sdks", - "prettier.prettierPath": ".yarn/sdks/prettier/index.cjs" + "prettier.prettierPath": ".yarn/sdks/prettier/index.cjs", + "json.schemas": [ + { + "fileMatch": ["src/*/*/manifest.json"], + "url": "./lib/manifest.schema.json" + } + ] } diff --git a/eslint.config.mjs b/eslint.config.mjs index bc0b335..1932f0a 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -65,6 +65,7 @@ export default defineConfig([ }, rules: { '@typescript-eslint/ban-ts-comment': 'warn', + '@typescript-eslint/no-explicit-any': 'off', // was error '@typescript-eslint/no-import-type-side-effects': 'error', '@typescript-eslint/no-namespace': 'off', '@typescript-eslint/no-unnecessary-type-assertion': 'error', diff --git a/lib/__tests__/build.test.ts b/lib/__tests__/build.test.ts new file mode 100644 index 0000000..7a65be1 --- /dev/null +++ b/lib/__tests__/build.test.ts @@ -0,0 +1,141 @@ +import { exec, type ExecException } from 'child_process'; +import fs from 'fs/promises'; +import pathlib from 'path'; +import { Command } from '@commander-js/extra-typings'; +import { beforeEach, describe, expect, it, test, vi } from 'vitest'; +import { generateManifest } from '../build.js'; + +const repoRoot = pathlib.resolve(import.meta.dirname, '..', '..'); + +vi.mock(import('fs/promises'), () => ({ + default: { + cp: vi.fn(), + mkdir: vi.fn(), + writeFile: vi.fn(), + } as any, +})); + +vi.mock( + import('child_process'), + () => + ({ + spawn: vi.fn(), + exec: vi.fn(), + }) as any, +); + +vi.mock(import('@commander-js/extra-typings'), async actual => { + const commander = await actual(); + + return { + ...commander, + Command: vi.fn(commander.Command), + } as any; +}); + +function mockExec(stdout: string, err: ExecException | null) { + vi.mocked(exec).mockImplementationOnce((cmd, cb) => { + (cb as (...args: any[]) => any)(err, stdout, ''); + return {} as any; + }); +} + +/** + * Utility function to "mock" the workspaces present in the repo + */ +function mockWorkspaces(workspaces: [string, string, 'external' | 'installable'][]) { + const workspaceStrs: string[] = []; + + for (const [loc, name, type] of workspaces) { + workspaceStrs.push(`{"location":"${loc}","name":"${name}"}`); + const workspaceDir = pathlib.posix.join(repoRoot, loc); + + vi.doMock(import(`file://${workspaceDir}/package.json`), () => ({ + default: { + name, + description: `This is the ${name} plugin`, + }, + })); + + vi.doMock(import(`file://${workspaceDir}/manifest.json`), () => ({ + default: { + type, + }, + })); + } + + const workspaceStr = workspaces + .map(([loc, name]) => `{"location":"${loc}","name":"${name}"}`) + .join('\n'); + mockExec(workspaceStr, null); +} + +/** + * Checks if `fs.writeFile` was called with the given path and returns the string + * that was written to the given path. Throws an error if `fs.writeFile` was never + * called with the given path. + */ +const findCall = vi.defineHelper((path: string) => { + const { + mock: { calls }, + } = vi.mocked(fs.writeFile); + + const result = calls.find(([writtenPath]) => writtenPath === path); + + if (!result) { + expect.fail(`fs.writeFile was not called with ${path}`); + } + + return JSON.parse(result[1] as string); +}); + +beforeEach(() => { + vi.clearAllMocks(); +}); + +test('Commander should not have been called', () => { + expect(Command).not.toHaveBeenCalled(); +}); + +describe(generateManifest, () => { + mockWorkspaces([ + ['src/common/test', '@sourceacademy/common-test', 'installable'], + ['src/web/test', '@sourceacademy/web-test', 'external'], + ['src/runner/test', '@sourceacademy/runner-test', 'installable'], + ]); + + it('works', async () => { + await generateManifest(); + + expect(fs.mkdir).toHaveBeenCalledExactlyOnceWith(expect.any(String), { recursive: true }); + expect(exec).toHaveBeenCalledOnce(); + + // once for each plugin type, once for plugin directory and once for + // changeset + expect(fs.writeFile).toHaveBeenCalledTimes(5); + + // Check for plugin directory + const pluginDirectory = findCall(pathlib.join(repoRoot, 'dist', 'directory.json')); + expect(pluginDirectory).toHaveLength(1); + expect(pluginDirectory[0]).toHaveProperty( + 'description', + 'This is the @sourceacademy/web-test plugin', + ); + + // Check commons directory + const commonDirectory = findCall(pathlib.join(repoRoot, 'dist', 'common.json')); + expect(commonDirectory).toEqual({ test: { type: 'installable' } }); + + // Check web directory + const webDirectory = findCall(pathlib.join(repoRoot, 'dist', 'web.json')); + expect(webDirectory).toEqual({ test: { type: 'external' } }); + + // Check runner directory + const runnerDirectory = findCall(pathlib.join(repoRoot, 'dist', 'runner.json')); + expect(runnerDirectory).toEqual({ test: { type: 'installable' } }); + + // Check for changeset + const { ignore: ignoreList } = findCall(pathlib.join(repoRoot, '.changeset', 'config.json')); + expect(ignoreList).toContain('@sourceacademy/web-test'); + }); +}); diff --git a/lib/build.ts b/lib/build.ts index e6c28eb..27fb666 100644 --- a/lib/build.ts +++ b/lib/build.ts @@ -2,8 +2,8 @@ import { exec, spawn } from 'child_process'; import fs from 'fs/promises'; import { createRequire } from 'module'; import pathlib from 'path'; -import type { IPluginDefinition } from '@sourceacademy/plugin-directory/dist/types/IPluginDefinition.js'; import { Command } from '@commander-js/extra-typings'; +import type { IPluginDefinition } from '@sourceacademy/plugin-directory/dist/types/IPluginDefinition.js'; import packageJSON from '../package.json' with { type: 'json' }; interface WorkspaceEntry { @@ -15,6 +15,12 @@ interface Manifest { type: 'installable' | 'external'; } +const repoRoot = pathlib.resolve(import.meta.dirname, '..'); +// Create a `require` function to use node's resolution algorithm to find +// the entry point of plugins +const require = createRequire(import.meta.url); +const pluginDirectory: Record = {}; + /** * Use `yarn workspaces list --json` to gather all the workspaces in the repository */ @@ -34,12 +40,6 @@ async function getWorkspaces() { return entries.map(each => JSON.parse(each)) as WorkspaceEntry[]; } -const repoRoot = pathlib.resolve(import.meta.dirname, '..'); -// Create a `require` function to use node's resolution algorithm to find -// the entry point of plugins -const require = createRequire(import.meta.url); -const pluginDirectory: Record = {}; - /** * Generates the manifest files for each plugin and updates the changeset config to ignore non-installable plugins. * The manifest files are generated by reading the manifest.json file in each plugin folder and writing it to the dist folder. @@ -250,19 +250,19 @@ async function clean() { await Promise.all(promises); } -const program = new Command() - .addCommand( - new Command('build') - .allowExcessArguments(true) - .allowUnknownOption(true) - .action(async (_, cmd) => { - await build(cmd.args); - }), - { isDefault: true }, - ) - .addCommand(new Command('clean').description('Remove dist folders').action(clean)); - // Only run automatically if not testing if (process.env.NODE_ENV !== 'test') { + const program = new Command() + .addCommand( + new Command('build') + .allowExcessArguments(true) + .allowUnknownOption(true) + .action(async (_, cmd) => { + await build(cmd.args); + }), + { isDefault: true }, + ) + .addCommand(new Command('clean').description('Remove dist folders').action(clean)); + await program.parseAsync(); } diff --git a/lib/manifest.schema.json b/lib/manifest.schema.json new file mode 100644 index 0000000..ce7c879 --- /dev/null +++ b/lib/manifest.schema.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "type": { + "description": "Type of plugin", + "enum": ["installable", "external"] + } + }, + "additionalProperties": false +} From b850445cb794c9f0f88f7d14ca90d4f27d474db4 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Tue, 18 Aug 2026 00:24:04 +0800 Subject: [PATCH 20/29] Remove unused eslint directives --- src/common/autocomplete/src/syntax.ts | 1 - src/runner/autocomplete/src/index.ts | 1 - src/runner/cse-machine/src/index.ts | 1 - src/runner/module-loader/src/index.ts | 1 - src/runner/remoteExecution/index.ts | 1 - src/runner/test/src/index.ts | 1 - src/web/autocomplete/src/index.ts | 1 - src/web/cse-machine/src/index.ts | 1 - src/web/module-loader/src/index.ts | 1 - src/web/stepper/src/SubstVisualizer.tsx | 1 - src/web/test/src/index.tsx | 1 - 11 files changed, 11 deletions(-) diff --git a/src/common/autocomplete/src/syntax.ts b/src/common/autocomplete/src/syntax.ts index 30bdc74..cd3c6b7 100644 --- a/src/common/autocomplete/src/syntax.ts +++ b/src/common/autocomplete/src/syntax.ts @@ -28,7 +28,6 @@ export interface ModeHook { * The arguments and return value must be structured-cloneable. */ // Ace mode extensions have different signatures, so the RPC boundary must accept arbitrary arguments. -// eslint-disable-next-line @typescript-eslint/no-explicit-any export type ModeRpcFunction = (...args: any[]) => unknown; /** A mode function can either reuse an Ace implementation or run remotely. */ diff --git a/src/runner/autocomplete/src/index.ts b/src/runner/autocomplete/src/index.ts index 2a30d9e..f06aa18 100644 --- a/src/runner/autocomplete/src/index.ts +++ b/src/runner/autocomplete/src/index.ts @@ -53,7 +53,6 @@ export abstract class BaseAutoCompleteRunnerPlugin implements IPlugin { constructor( _conduit: IConduit, - // eslint-disable-next-line @typescript-eslint/no-explicit-any [autoCompleteChannel, syntaxHighlightChannel]: IChannel[], ) { this.__autoCompleteChannel = autoCompleteChannel; diff --git a/src/runner/cse-machine/src/index.ts b/src/runner/cse-machine/src/index.ts index d0f065a..c29120a 100644 --- a/src/runner/cse-machine/src/index.ts +++ b/src/runner/cse-machine/src/index.ts @@ -27,7 +27,6 @@ export class CseMachinePlugin implements IPlugin { constructor( _conduit: IConduit, - // eslint-disable-next-line @typescript-eslint/no-explicit-any [cseChannel]: IChannel[], ) { if (!cseChannel) { diff --git a/src/runner/module-loader/src/index.ts b/src/runner/module-loader/src/index.ts index d3e1c94..587b0e4 100644 --- a/src/runner/module-loader/src/index.ts +++ b/src/runner/module-loader/src/index.ts @@ -24,7 +24,6 @@ export class ModuleLoaderRunnerPlugin implements IPlugin { constructor( conduit: IConduit, - // eslint-disable-next-line @typescript-eslint/no-explicit-any [moduleRequestChannel]: IChannel[], conductor: IRunnerPlugin, evaluator: IInterfacableEvaluator, diff --git a/src/runner/remoteExecution/index.ts b/src/runner/remoteExecution/index.ts index 271b1c3..9e9778a 100644 --- a/src/runner/remoteExecution/index.ts +++ b/src/runner/remoteExecution/index.ts @@ -8,7 +8,6 @@ export class remoteRunnerPlugin implements IPlugin { private readonly __channel: IChannel; private readonly engine: EV3Engine; - // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor(_conduit: IConduit, [channel]: IChannel[]) { this.__channel = channel; this.engine = new EV3Engine(); diff --git a/src/runner/test/src/index.ts b/src/runner/test/src/index.ts index 71a5f15..39f32d7 100644 --- a/src/runner/test/src/index.ts +++ b/src/runner/test/src/index.ts @@ -12,7 +12,6 @@ export abstract class TestPlugin implements IPlugin { private readonly __testChannel: IChannel; constructor( _conduit: IConduit, - // eslint-disable-next-line @typescript-eslint/no-explicit-any [testChannel]: IChannel[], ) { this.__testChannel = testChannel; diff --git a/src/web/autocomplete/src/index.ts b/src/web/autocomplete/src/index.ts index 871c91c..4fa2e21 100644 --- a/src/web/autocomplete/src/index.ts +++ b/src/web/autocomplete/src/index.ts @@ -67,7 +67,6 @@ export abstract class BaseAutoCompleteWebPlugin implements IPlugin { */ abstract loadMode(data: SyntaxHighlightData): void; - // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor(_conduit: IConduit, [autoCompleteChannel, syntaxChannel]: IChannel[]) { this.__autoCompleteChannel = autoCompleteChannel; this.__syntaxChannel = syntaxChannel; diff --git a/src/web/cse-machine/src/index.ts b/src/web/cse-machine/src/index.ts index c379e93..319620c 100644 --- a/src/web/cse-machine/src/index.ts +++ b/src/web/cse-machine/src/index.ts @@ -42,7 +42,6 @@ export abstract class CseMachineHostPlugin implements IPlugin { constructor( _conduit: IConduit, - // eslint-disable-next-line @typescript-eslint/no-explicit-any [cseChannel]: IChannel[], ) { if (!cseChannel) { diff --git a/src/web/module-loader/src/index.ts b/src/web/module-loader/src/index.ts index fca5fce..ec41c1a 100644 --- a/src/web/module-loader/src/index.ts +++ b/src/web/module-loader/src/index.ts @@ -26,7 +26,6 @@ export class ModuleLoaderWebPlugin implements IPlugin { constructor( _conduit: IConduit, - // eslint-disable-next-line @typescript-eslint/no-explicit-any [moduleRequestChannel]: IChannel[], ) { this.__moduleRequestChannel = moduleRequestChannel; diff --git a/src/web/stepper/src/SubstVisualizer.tsx b/src/web/stepper/src/SubstVisualizer.tsx index ae2ffe8..4266a55 100644 --- a/src/web/stepper/src/SubstVisualizer.tsx +++ b/src/web/stepper/src/SubstVisualizer.tsx @@ -27,7 +27,6 @@ import { injectStepperStyles } from './styles'; * language-specific fields (e.g. `left`, `operator`, `params`) without per-node typing, exactly as * the original (class-based) renderer did after casting. */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -- language-specific AST fields are read untyped, exactly as the original class-based renderer did after casting type StepperNode = SerializedStepperNode & Record; function SubstDefaultText() { diff --git a/src/web/test/src/index.tsx b/src/web/test/src/index.tsx index fb3ed40..e4bd2a7 100644 --- a/src/web/test/src/index.tsx +++ b/src/web/test/src/index.tsx @@ -48,7 +48,6 @@ export abstract class TestPlugin implements IPlugin { private readonly __testChannel: IChannel; constructor( _conduit: IConduit, - // eslint-disable-next-line @typescript-eslint/no-explicit-any [testChannel]: IChannel[], tabService: ITabService, ) { From 77eb594dbe5c854c8702440ad9b35c5c137b7ee9 Mon Sep 17 00:00:00 2001 From: "LAPTOP-1A6BH9TL\\Lee Yi" Date: Tue, 18 Aug 2026 10:02:27 +0800 Subject: [PATCH 21/29] Run format --- src/runner/autocomplete/src/index.ts | 5 +---- src/runner/cse-machine/src/index.ts | 5 +---- src/runner/test/src/index.ts | 5 +---- src/web/cse-machine/src/index.ts | 5 +---- src/web/module-loader/src/index.ts | 5 +---- src/web/test/src/index.tsx | 6 +----- 6 files changed, 6 insertions(+), 25 deletions(-) diff --git a/src/runner/autocomplete/src/index.ts b/src/runner/autocomplete/src/index.ts index f06aa18..df7efd8 100644 --- a/src/runner/autocomplete/src/index.ts +++ b/src/runner/autocomplete/src/index.ts @@ -51,10 +51,7 @@ export abstract class BaseAutoCompleteRunnerPlugin implements IPlugin { */ abstract autocomplete(code: string, row: number, column: number): AutoCompleteEntry[]; - constructor( - _conduit: IConduit, - [autoCompleteChannel, syntaxHighlightChannel]: IChannel[], - ) { + constructor(_conduit: IConduit, [autoCompleteChannel, syntaxHighlightChannel]: IChannel[]) { this.__autoCompleteChannel = autoCompleteChannel; this.__syntaxHighlightChannel = syntaxHighlightChannel; diff --git a/src/runner/cse-machine/src/index.ts b/src/runner/cse-machine/src/index.ts index c29120a..0e046df 100644 --- a/src/runner/cse-machine/src/index.ts +++ b/src/runner/cse-machine/src/index.ts @@ -25,10 +25,7 @@ export class CseMachinePlugin implements IPlugin { private readonly __cseChannel: IChannel; - constructor( - _conduit: IConduit, - [cseChannel]: IChannel[], - ) { + constructor(_conduit: IConduit, [cseChannel]: IChannel[]) { if (!cseChannel) { throw new Error('CSE channel is required but was not provided.'); } diff --git a/src/runner/test/src/index.ts b/src/runner/test/src/index.ts index 39f32d7..64cf857 100644 --- a/src/runner/test/src/index.ts +++ b/src/runner/test/src/index.ts @@ -10,10 +10,7 @@ export abstract class TestPlugin implements IPlugin { readonly id: string = RUNNER_ID; static readonly channelAttach = [CHANNEL_ID]; private readonly __testChannel: IChannel; - constructor( - _conduit: IConduit, - [testChannel]: IChannel[], - ) { + constructor(_conduit: IConduit, [testChannel]: IChannel[]) { this.__testChannel = testChannel; this.__testChannel.subscribe(message => { console.log(message); diff --git a/src/web/cse-machine/src/index.ts b/src/web/cse-machine/src/index.ts index 319620c..87c57ba 100644 --- a/src/web/cse-machine/src/index.ts +++ b/src/web/cse-machine/src/index.ts @@ -40,10 +40,7 @@ export abstract class CseMachineHostPlugin implements IPlugin { */ abstract receiveSnapshots(snapshots: CseSnapshot[], breakpointSteps: number[]): void; - constructor( - _conduit: IConduit, - [cseChannel]: IChannel[], - ) { + constructor(_conduit: IConduit, [cseChannel]: IChannel[]) { if (!cseChannel) { throw new Error('CSE channel is required but was not provided.'); } diff --git a/src/web/module-loader/src/index.ts b/src/web/module-loader/src/index.ts index ec41c1a..b2f0014 100644 --- a/src/web/module-loader/src/index.ts +++ b/src/web/module-loader/src/index.ts @@ -24,10 +24,7 @@ export class ModuleLoaderWebPlugin implements IPlugin { private moduleDirectoryURL: string | null = null; private moduleDirectory: ModuleDirectory | null = null; - constructor( - _conduit: IConduit, - [moduleRequestChannel]: IChannel[], - ) { + constructor(_conduit: IConduit, [moduleRequestChannel]: IChannel[]) { this.__moduleRequestChannel = moduleRequestChannel; ModuleLoaderWebPlugin.instance = this; this.__moduleRequestChannel.subscribe(message => { diff --git a/src/web/test/src/index.tsx b/src/web/test/src/index.tsx index e4bd2a7..f61b919 100644 --- a/src/web/test/src/index.tsx +++ b/src/web/test/src/index.tsx @@ -46,11 +46,7 @@ export abstract class TestPlugin implements IPlugin { readonly id: string = WEB_ID; static readonly channelAttach = [CHANNEL_ID]; private readonly __testChannel: IChannel; - constructor( - _conduit: IConduit, - [testChannel]: IChannel[], - tabService: ITabService, - ) { + constructor(_conduit: IConduit, [testChannel]: IChannel[], tabService: ITabService) { this.__testChannel = testChannel; this.__testChannel.subscribe(message => { console.log(message); From bd9dcc302861d656e4311df9bdfb4a821561af37 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Tue, 25 Aug 2026 22:52:49 +0800 Subject: [PATCH 22/29] Add tests for transformSingle --- lib/__tests__/build.test.ts | 43 +++++++++++++++++++++++++++++++------ lib/build.ts | 9 ++++---- package.json | 2 +- 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/lib/__tests__/build.test.ts b/lib/__tests__/build.test.ts index 7a65be1..8efb66b 100644 --- a/lib/__tests__/build.test.ts +++ b/lib/__tests__/build.test.ts @@ -3,7 +3,7 @@ import fs from 'fs/promises'; import pathlib from 'path'; import { Command } from '@commander-js/extra-typings'; import { beforeEach, describe, expect, it, test, vi } from 'vitest'; -import { generateManifest } from '../build.js'; +import { generateManifest, transformSingle } from '../build.js'; const repoRoot = pathlib.resolve(import.meta.dirname, '..', '..'); @@ -12,6 +12,7 @@ vi.mock(import('fs/promises'), () => ({ cp: vi.fn(), mkdir: vi.fn(), writeFile: vi.fn(), + readFile: vi.fn() } as any, })); @@ -86,7 +87,7 @@ const findCall = vi.defineHelper((path: string) => { expect.fail(`fs.writeFile was not called with ${path}`); } - return JSON.parse(result[1] as string); + return result[1] as string; }); beforeEach(() => { @@ -115,7 +116,7 @@ describe(generateManifest, () => { expect(fs.writeFile).toHaveBeenCalledTimes(5); // Check for plugin directory - const pluginDirectory = findCall(pathlib.join(repoRoot, 'dist', 'directory.json')); + const pluginDirectory = JSON.parse(findCall(pathlib.join(repoRoot, 'dist', 'directory.json'))); expect(pluginDirectory).toHaveLength(1); expect(pluginDirectory[0]).toHaveProperty( 'description', @@ -123,19 +124,47 @@ describe(generateManifest, () => { ); // Check commons directory - const commonDirectory = findCall(pathlib.join(repoRoot, 'dist', 'common.json')); + const commonDirectory = JSON.parse(findCall(pathlib.join(repoRoot, 'dist', 'common.json'))); expect(commonDirectory).toEqual({ test: { type: 'installable' } }); // Check web directory - const webDirectory = findCall(pathlib.join(repoRoot, 'dist', 'web.json')); + const webDirectory = JSON.parse(findCall(pathlib.join(repoRoot, 'dist', 'web.json'))); expect(webDirectory).toEqual({ test: { type: 'external' } }); // Check runner directory - const runnerDirectory = findCall(pathlib.join(repoRoot, 'dist', 'runner.json')); + const runnerDirectory = JSON.parse(findCall(pathlib.join(repoRoot, 'dist', 'runner.json'))); expect(runnerDirectory).toEqual({ test: { type: 'installable' } }); // Check for changeset - const { ignore: ignoreList } = findCall(pathlib.join(repoRoot, '.changeset', 'config.json')); + const { ignore: ignoreList } = JSON.parse(findCall(pathlib.join(repoRoot, '.changeset', 'config.json'))); expect(ignoreList).toContain('@sourceacademy/web-test'); }); }); + +describe(transformSingle, () => { + it('works', async () => { + vi.mocked(fs.readFile).mockResolvedValueOnce(` + module.exports = { + foo: () => 'foo', + bar: () => { + const value = require('value'); + return value; + } + } + `) + + await expect(transformSingle('path.js')).resolves.toBeUndefined(); + expect(fs.readFile).toHaveBeenCalledOnce(); + + const transformedContents = findCall('path.js'); + const dataUri = 'data:text/javascript,' + encodeURIComponent(transformedContents); + const { default: func } = await import(dataUri); + + expect(func).toBeInstanceOf(Function); + + const { foo, bar } = func((p: string) => p === 'value'); + + expect(foo()).toEqual('foo'); + expect(bar()).toEqual(true); + }); +}); diff --git a/lib/build.ts b/lib/build.ts index 27fb666..9f6dc63 100644 --- a/lib/build.ts +++ b/lib/build.ts @@ -2,8 +2,8 @@ import { exec, spawn } from 'child_process'; import fs from 'fs/promises'; import { createRequire } from 'module'; import pathlib from 'path'; -import { Command } from '@commander-js/extra-typings'; import type { IPluginDefinition } from '@sourceacademy/plugin-directory/dist/types/IPluginDefinition.js'; +import { Command } from '@commander-js/extra-typings'; import packageJSON from '../package.json' with { type: 'json' }; interface WorkspaceEntry { @@ -187,10 +187,7 @@ async function copyDistFiles(globalManifest: Record Date: Wed, 26 Aug 2026 21:40:29 +0800 Subject: [PATCH 23/29] Run format --- lib/__tests__/build.test.ts | 8 +++++--- yarn.config.cjs | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/__tests__/build.test.ts b/lib/__tests__/build.test.ts index 8efb66b..d56c09f 100644 --- a/lib/__tests__/build.test.ts +++ b/lib/__tests__/build.test.ts @@ -12,7 +12,7 @@ vi.mock(import('fs/promises'), () => ({ cp: vi.fn(), mkdir: vi.fn(), writeFile: vi.fn(), - readFile: vi.fn() + readFile: vi.fn(), } as any, })); @@ -136,7 +136,9 @@ describe(generateManifest, () => { expect(runnerDirectory).toEqual({ test: { type: 'installable' } }); // Check for changeset - const { ignore: ignoreList } = JSON.parse(findCall(pathlib.join(repoRoot, '.changeset', 'config.json'))); + const { ignore: ignoreList } = JSON.parse( + findCall(pathlib.join(repoRoot, '.changeset', 'config.json')), + ); expect(ignoreList).toContain('@sourceacademy/web-test'); }); }); @@ -151,7 +153,7 @@ describe(transformSingle, () => { return value; } } - `) + `); await expect(transformSingle('path.js')).resolves.toBeUndefined(); expect(fs.readFile).toHaveBeenCalledOnce(); diff --git a/yarn.config.cjs b/yarn.config.cjs index a541006..7463aee 100644 --- a/yarn.config.cjs +++ b/yarn.config.cjs @@ -2,6 +2,7 @@ const pathlib = require('path'); const { defineConfig } = require('@yarnpkg/types'); +const { repository } = require('./package.json'); module.exports = defineConfig({ async constraints({ Yarn }) { @@ -13,8 +14,7 @@ module.exports = defineConfig({ // Check that the repository field for each workspace is correct workspace.set('repository', { - type: 'git', - url: 'git+https://github.com/source-academy/plugins', + ...repository, directory: normedPath, }); } From 77e6a9a540960f0afd944b37a5e0e1bdf81d36f2 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Wed, 26 Aug 2026 21:44:53 +0800 Subject: [PATCH 24/29] Fix constraints problem --- src/common/autocomplete/package.json | 2 +- src/common/cse-machine/package.json | 2 +- src/common/data-visualizer/package.json | 2 +- src/common/module-loader/package.json | 2 +- src/common/stepper/package.json | 2 +- src/common/tabs/package.json | 2 +- src/common/test/package.json | 2 +- src/runner/autocomplete/package.json | 2 +- src/runner/cse-machine/package.json | 2 +- src/runner/data-visualizer/package.json | 2 +- src/runner/module-loader/package.json | 2 +- src/runner/remoteExecution/package.json | 2 +- src/runner/stepper/package.json | 2 +- src/runner/test/package.json | 2 +- src/web/autocomplete/package.json | 2 +- src/web/cse-machine/package.json | 2 +- src/web/data-visualizer/package.json | 2 +- src/web/module-loader/package.json | 2 +- src/web/stepper/package.json | 2 +- src/web/test/package.json | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/common/autocomplete/package.json b/src/common/autocomplete/package.json index 5e31088..b67683b 100644 --- a/src/common/autocomplete/package.json +++ b/src/common/autocomplete/package.json @@ -32,7 +32,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/common/autocomplete" } } diff --git a/src/common/cse-machine/package.json b/src/common/cse-machine/package.json index 1a76709..8c3e7ed 100644 --- a/src/common/cse-machine/package.json +++ b/src/common/cse-machine/package.json @@ -3,7 +3,7 @@ "version": "0.3.0", "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/common/cse-machine" }, "packageManager": "yarn@4.18.0", diff --git a/src/common/data-visualizer/package.json b/src/common/data-visualizer/package.json index 70497af..ae601c4 100644 --- a/src/common/data-visualizer/package.json +++ b/src/common/data-visualizer/package.json @@ -34,7 +34,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/common/data-visualizer" } } diff --git a/src/common/module-loader/package.json b/src/common/module-loader/package.json index 0c78c7b..e6d7aa6 100644 --- a/src/common/module-loader/package.json +++ b/src/common/module-loader/package.json @@ -5,7 +5,7 @@ "description": "The common types for loading modules in Source Academy", "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/common/module-loader" }, "scripts": { diff --git a/src/common/stepper/package.json b/src/common/stepper/package.json index 9a2dd9c..3c1d0a6 100644 --- a/src/common/stepper/package.json +++ b/src/common/stepper/package.json @@ -5,7 +5,7 @@ "description": "Shared, language-agnostic protocol for the Source Academy stepper plugin pair", "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/common/stepper" }, "scripts": { diff --git a/src/common/tabs/package.json b/src/common/tabs/package.json index 52e18f8..0e6894b 100644 --- a/src/common/tabs/package.json +++ b/src/common/tabs/package.json @@ -5,7 +5,7 @@ "description": "Common utilities for tab loading in Source Academy", "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/common/tabs" }, "scripts": { diff --git a/src/common/test/package.json b/src/common/test/package.json index 87bca3d..2ab8b5f 100644 --- a/src/common/test/package.json +++ b/src/common/test/package.json @@ -5,7 +5,7 @@ "description": "The test common plugin for the plugins repository", "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/common/test" }, "scripts": { diff --git a/src/runner/autocomplete/package.json b/src/runner/autocomplete/package.json index 0ca2f50..3b9eefd 100644 --- a/src/runner/autocomplete/package.json +++ b/src/runner/autocomplete/package.json @@ -42,7 +42,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/runner/autocomplete" } } diff --git a/src/runner/cse-machine/package.json b/src/runner/cse-machine/package.json index 65beadf..dd0d962 100644 --- a/src/runner/cse-machine/package.json +++ b/src/runner/cse-machine/package.json @@ -5,7 +5,7 @@ "description": "The runner-side CSE machine plugin: sends evaluation snapshots to the host", "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/runner/cse-machine" }, "scripts": { diff --git a/src/runner/data-visualizer/package.json b/src/runner/data-visualizer/package.json index d7d1b68..a9fe582 100644 --- a/src/runner/data-visualizer/package.json +++ b/src/runner/data-visualizer/package.json @@ -5,7 +5,7 @@ "description": "Language-agnostic runner plugin for the Source Academy data visualizer", "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/runner/data-visualizer" }, "scripts": { diff --git a/src/runner/module-loader/package.json b/src/runner/module-loader/package.json index 5232d42..714e998 100644 --- a/src/runner/module-loader/package.json +++ b/src/runner/module-loader/package.json @@ -5,7 +5,7 @@ "description": "The runner plugin for the module loader in Source Academy", "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/runner/module-loader" }, "scripts": { diff --git a/src/runner/remoteExecution/package.json b/src/runner/remoteExecution/package.json index 35118d7..1f6ece5 100644 --- a/src/runner/remoteExecution/package.json +++ b/src/runner/remoteExecution/package.json @@ -4,7 +4,7 @@ "license": "ISC", "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/runner/remoteExecution" }, "publishConfig": { diff --git a/src/runner/stepper/package.json b/src/runner/stepper/package.json index b47b69d..ba1a280 100644 --- a/src/runner/stepper/package.json +++ b/src/runner/stepper/package.json @@ -5,7 +5,7 @@ "description": "Language-agnostic runner plugin for the Source Academy stepper", "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/runner/stepper" }, "scripts": { diff --git a/src/runner/test/package.json b/src/runner/test/package.json index ce0220c..7251385 100644 --- a/src/runner/test/package.json +++ b/src/runner/test/package.json @@ -5,7 +5,7 @@ "description": "The test runner plugin for the plugins repository", "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/runner/test" }, "scripts": { diff --git a/src/web/autocomplete/package.json b/src/web/autocomplete/package.json index eff8c53..08356a3 100644 --- a/src/web/autocomplete/package.json +++ b/src/web/autocomplete/package.json @@ -43,7 +43,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/web/autocomplete" } } diff --git a/src/web/cse-machine/package.json b/src/web/cse-machine/package.json index 8f48f1e..483d663 100644 --- a/src/web/cse-machine/package.json +++ b/src/web/cse-machine/package.json @@ -5,7 +5,7 @@ "description": "The web/host-side CSE machine plugin: receives evaluation snapshots for rendering", "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/web/cse-machine" }, "scripts": { diff --git a/src/web/data-visualizer/package.json b/src/web/data-visualizer/package.json index 164bec0..6080572 100644 --- a/src/web/data-visualizer/package.json +++ b/src/web/data-visualizer/package.json @@ -5,7 +5,7 @@ "description": "Host (web) plugin for the Source Academy data visualizer: classifies and renders drawn structures", "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/web/data-visualizer" }, "scripts": { diff --git a/src/web/module-loader/package.json b/src/web/module-loader/package.json index 2b6eb28..077ff12 100644 --- a/src/web/module-loader/package.json +++ b/src/web/module-loader/package.json @@ -5,7 +5,7 @@ "description": "The web plugin for the module loader in Source Academy", "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/web/module-loader" }, "scripts": { diff --git a/src/web/stepper/package.json b/src/web/stepper/package.json index ba6fe64..ae0f2db 100644 --- a/src/web/stepper/package.json +++ b/src/web/stepper/package.json @@ -5,7 +5,7 @@ "description": "Host (web) plugin for the Source Academy stepper: renders evaluation steps", "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/web/stepper" }, "scripts": { diff --git a/src/web/test/package.json b/src/web/test/package.json index abeeb5f..2d4ab98 100644 --- a/src/web/test/package.json +++ b/src/web/test/package.json @@ -5,7 +5,7 @@ "description": "The test web plugin for the plugins repository", "repository": { "type": "git", - "url": "git+https://github.com/source-academy/plugins", + "url": "git+https://github.com/source-academy/plugins.git", "directory": "src/web/test" }, "scripts": { From a08e401e6276c858b2e8d2ae762a3c755dcb6314 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Wed, 26 Aug 2026 21:50:42 +0800 Subject: [PATCH 25/29] Fix linting --- eslint.config.mjs | 2 +- src/web/data-visualizer/src/tree/ClassicDrawer.tsx | 8 ++++---- src/web/data-visualizer/src/tree/FunctionTreeNode.tsx | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 1932f0a..7e87bf2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -8,7 +8,7 @@ import tseslint from 'typescript-eslint'; export default defineConfig([ { - ignores: ['**/dist', '**/node_modules', '**/coverage'], + ignores: ['.yarn', '.pnp*', '**/dist', '**/node_modules', '**/coverage'], }, eslintConfigPrettierFlat, { diff --git a/src/web/data-visualizer/src/tree/ClassicDrawer.tsx b/src/web/data-visualizer/src/tree/ClassicDrawer.tsx index 80edd96..97fc4f8 100644 --- a/src/web/data-visualizer/src/tree/ClassicDrawer.tsx +++ b/src/web/data-visualizer/src/tree/ClassicDrawer.tsx @@ -97,10 +97,10 @@ export class ClassicDrawer { y: number, parentX: number, parentY: number, - colorIndex: number, - parentIndex: number, - originIndex: number, - originX: number, + _colorIndex: number, + _parentIndex: number, + _originIndex: number, + _originX: number, ) { if (node instanceof AlreadyParsedTreeNode) { // if its child is part of a cycle and it's been drawn, link back to that node instead diff --git a/src/web/data-visualizer/src/tree/FunctionTreeNode.tsx b/src/web/data-visualizer/src/tree/FunctionTreeNode.tsx index ca793ae..91c2a6e 100644 --- a/src/web/data-visualizer/src/tree/FunctionTreeNode.tsx +++ b/src/web/data-visualizer/src/tree/FunctionTreeNode.tsx @@ -13,7 +13,7 @@ export class FunctionTreeNode extends DrawableTreeNode { y: number, parentX: number, parentY: number, - colorIndex: number, + _colorIndex: number, ): React.ReactElement { this._drawable = ( From 676abbf64a49341383a3e86764ba9393a46c695f Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Wed, 26 Aug 2026 22:41:29 +0800 Subject: [PATCH 26/29] Continue configuring test for lib to ensure coverage --- lib/__tests__/build.test.ts | 62 +++++++++++++++++++++++++++++++++++-- lib/build.ts | 8 +++-- vitest.config.ts | 2 +- 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/lib/__tests__/build.test.ts b/lib/__tests__/build.test.ts index d56c09f..e9f7e81 100644 --- a/lib/__tests__/build.test.ts +++ b/lib/__tests__/build.test.ts @@ -1,9 +1,9 @@ -import { exec, type ExecException } from 'child_process'; +import { exec, spawn, type ExecException } from 'child_process'; import fs from 'fs/promises'; import pathlib from 'path'; import { Command } from '@commander-js/extra-typings'; import { beforeEach, describe, expect, it, test, vi } from 'vitest'; -import { generateManifest, transformSingle } from '../build.js'; +import { copyDistFiles, generateManifest, runYarnUsingSpawn, transformSingle } from '../build.js'; const repoRoot = pathlib.resolve(import.meta.dirname, '..', '..'); @@ -170,3 +170,61 @@ describe(transformSingle, () => { expect(bar()).toEqual(true); }); }); + +describe(runYarnUsingSpawn, () => { + function mockSpawn(retCode: number) { + vi.mocked(spawn).mockImplementationOnce( + () => + ({ + on(handler: string, listener: (...args: any[]) => any) { + if (handler === 'close') { + listener(retCode); + } + return this; + }, + }) as any, + ); + } + + it('calls yarn when not on windows', async () => { + mockSpawn(0); + vi.spyOn(process, 'platform', 'get').mockReturnValueOnce('openbsd'); + + await runYarnUsingSpawn(); + expect(spawn).toHaveBeenCalledExactlyOnceWith('yarn', [], expect.any(Object)); + }); + + it('calls yarn.cmd when on windows', async () => { + mockSpawn(0); + vi.spyOn(process, 'platform', 'get').mockReturnValueOnce('win32'); + + await runYarnUsingSpawn(); + expect(spawn).toHaveBeenCalledExactlyOnceWith('yarn.cmd', [], expect.any(Object)); + }); + + it('rejects with the error code', async () => { + mockSpawn(1); + + await expect(runYarnUsingSpawn()).rejects.toThrow('Process exited with code 1'); + }); +}); + +describe(copyDistFiles, () => { + mockWorkspaces([ + ['src/common/test', '@sourceacademy/common-test', 'installable'], + ['src/web/test', '@sourceacademy/web-test', 'external'], + ['src/runner/test', '@sourceacademy/runner-test', 'installable'], + ]); + + it('works', async () => { + const manifest = await generateManifest(); + await copyDistFiles(manifest); + + // Only copies external plugins + expect(fs.cp).toHaveBeenCalledExactlyOnceWith( + pathlib.join(repoRoot, 'src', 'web', 'test', 'dist'), + pathlib.join(repoRoot, 'dist', 'web', 'test'), + { recursive: true }, + ); + }); +}); diff --git a/lib/build.ts b/lib/build.ts index 9f6dc63..d7acf3c 100644 --- a/lib/build.ts +++ b/lib/build.ts @@ -140,7 +140,7 @@ export async function generateManifest() { /** * Runs `yarn` with the given arguments. */ -function runYarnUsingSpawn(...args: string[]) { +export function runYarnUsingSpawn(...args: string[]) { return new Promise((resolve, reject) => { const proc = spawn(process.platform === 'win32' ? 'yarn.cmd' : 'yarn', args, { stdio: 'inherit', @@ -164,7 +164,7 @@ function runYarnUsingSpawn(...args: string[]) { * @example * await copyDistFiles(); */ -async function copyDistFiles(globalManifest: Record>) { +export async function copyDistFiles(globalManifest: Record>) { await Promise.all( // Iterate over each plugin type (web, runner, common) Object.entries(globalManifest).map(async ([pluginType, manifest]) => { @@ -213,8 +213,9 @@ async function transform() { * Builds the plugins by generating the manifest and running the build script for each plugin. * The build script is run in topological order to ensure that dependencies are built before the plugins that depend on them. * @param extraArgs Extra arguments to pass to the build script of each plugin. For example, if you want to run the build script in watch mode, you can pass ["--watch"] as extraArgs. - * @example + * @example ``` * await build(["--watch"]); + * ``` * This will run the build script of each plugin with the --watch flag, which is useful for development. */ async function build(extraArgs: string[] = []) { @@ -249,6 +250,7 @@ async function clean() { await Promise.all(promises); } +/* istanbul ignore start */ // Only run automatically if not testing if (process.env.NODE_ENV !== 'test') { const program = new Command() diff --git a/vitest.config.ts b/vitest.config.ts index 8095c32..6c1d99d 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -4,7 +4,7 @@ export default defineConfig({ test: { coverage: { provider: 'istanbul', - reporter: 'lcov', + reporter: ['text', 'lcov'], exclude: ['**/dist'], }, }, From 07554635e2aa92f80daa60e4e40c6a298b5a22a0 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Sun, 18 Oct 2026 03:30:26 +0800 Subject: [PATCH 27/29] Fix linting and package issues --- .changeset/config.json | 7 +- src/common/data-visualizer/src/index.ts | 8 - src/common/e-stepper/jest.config.cjs | 14 - src/common/e-stepper/package.json | 16 +- src/common/e-stepper/rollup.config.mjs | 16 +- .../e-stepper/src/__tests__/common.test.ts | 4 +- src/common/e-stepper/tsconfig.json | 3 +- src/runner/e-stepper/jest.config.cjs | 14 - src/runner/e-stepper/package.json | 18 +- src/runner/e-stepper/rollup.config.mjs | 16 +- .../e-stepper/src/__tests__/runner.test.ts | 4 +- src/runner/e-stepper/tsconfig.json | 3 +- src/runner/test/tsconfig.json | 4 +- .../src/__tests__/treeViews.test.ts | 29 +- src/web/e-stepper/package.json | 17 +- src/web/e-stepper/rollup.config.mjs | 12 +- src/web/e-stepper/src/EStepperHostPlugin.tsx | 2 +- src/web/e-stepper/src/EStepperView.tsx | 2 +- src/web/e-stepper/src/EnvDiagram.tsx | 6 +- .../src/__tests__/EStepperHostPlugin.test.tsx | 18 +- .../src/__tests__/EStepperView.test.tsx | 22 +- .../src/__tests__/EnvDiagram.test.tsx | 16 +- .../src/__tests__/ProgramArrows.test.tsx | 10 +- .../e-stepper/src/__tests__/layout.test.ts | 4 +- src/web/stepper/src/SubstVisualizer.tsx | 995 ------- .../src/__tests__/SubstVisualizer.test.tsx | 10 +- src/web/stepper/src/render.tsx | 2 +- yarn.lock | 2439 +---------------- 28 files changed, 171 insertions(+), 3540 deletions(-) delete mode 100644 src/common/e-stepper/jest.config.cjs delete mode 100644 src/runner/e-stepper/jest.config.cjs diff --git a/.changeset/config.json b/.changeset/config.json index 9bcd766..b1628ad 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -8,13 +8,8 @@ "baseBranch": "main", "updateInternalDependencies": "patch", "ignore": [ -<<<<<<< HEAD "@sourceacademy/web-data-visualizer", - "@sourceacademy/web-stepper" -======= - "@sourceacademy/web-stepper", "@sourceacademy/web-e-stepper", - "@sourceacademy/web-data-visualizer" ->>>>>>> origin/main + "@sourceacademy/web-stepper" ] } \ No newline at end of file diff --git a/src/common/data-visualizer/src/index.ts b/src/common/data-visualizer/src/index.ts index d4538fd..8e44107 100644 --- a/src/common/data-visualizer/src/index.ts +++ b/src/common/data-visualizer/src/index.ts @@ -59,13 +59,6 @@ export type RefId = number; * distinguishes "pair" from "list" for classification purposes by `children.length`, not by tag. */ export type SerializedDataVisualizerNode = -<<<<<<< HEAD - | { type: 'array'; refId: RefId; children: SerializedDataVisualizerNode[] } - | { type: 'empty' } - | { type: 'leaf'; displayValue: string; label: string } - | { type: 'function'; refId: RefId; displayValue: string } - | { type: 'ref'; refId: RefId }; -======= | { type: "array"; refId: RefId; children: SerializedDataVisualizerNode[] } | { type: "empty"; @@ -79,7 +72,6 @@ export type SerializedDataVisualizerNode = | { type: "leaf"; displayValue: string; label: string } | { type: "function"; refId: RefId; displayValue: string } | { type: "ref"; refId: RefId }; ->>>>>>> origin/main /** One row = the fully-serialized arguments of a single `draw_data(...)` call. */ export type SerializedDataVisualizerRow = SerializedDataVisualizerNode[]; diff --git a/src/common/e-stepper/jest.config.cjs b/src/common/e-stepper/jest.config.cjs deleted file mode 100644 index 06bb4a9..0000000 --- a/src/common/e-stepper/jest.config.cjs +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - preset: "ts-jest/presets/js-with-ts-esm", - testEnvironment: "node", - transform: { - "^.+\\.tsx?$": [ - "ts-jest", - { - useESM: true, - }, - ], - }, - testPathIgnorePatterns: [".*?dist/"], - coverageReporters: ["lcov"], -}; diff --git a/src/common/e-stepper/package.json b/src/common/e-stepper/package.json index f083c1b..e0b0a9c 100644 --- a/src/common/e-stepper/package.json +++ b/src/common/e-stepper/package.json @@ -28,16 +28,14 @@ "@sourceacademy/common-stepper": ">=0.0.4" }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@sourceacademy/common-cse-machine": "workspace:*", "@sourceacademy/common-stepper": "workspace:*", - "@types/jest": "^30.0.0", - "jest": "^30.4.2", - "rollup": "^4.60.2", - "ts-jest": "^29.4.11", - "tslib": "^2.8.1", - "typescript": "^6.0.3" + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/src/common/e-stepper/rollup.config.mjs b/src/common/e-stepper/rollup.config.mjs index 238a07c..05aaa22 100644 --- a/src/common/e-stepper/rollup.config.mjs +++ b/src/common/e-stepper/rollup.config.mjs @@ -1,11 +1,11 @@ +// @ts-check + import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from "rollup"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: [ { @@ -17,5 +17,9 @@ export default { format: "esm", }, ], - plugins: [nodeResolve(), typescript(), terser()], -}; + plugins: [ + nodeResolve(), + typescript({ exclude: ['**/__tests__/**'] }), + terser() + ], +}); diff --git a/src/common/e-stepper/src/__tests__/common.test.ts b/src/common/e-stepper/src/__tests__/common.test.ts index 4b08a60..4b9e855 100644 --- a/src/common/e-stepper/src/__tests__/common.test.ts +++ b/src/common/e-stepper/src/__tests__/common.test.ts @@ -1,10 +1,10 @@ +import { RUNNER_ID as STEPPER_RUNNER_ID, STEPPER_CHANNEL_ID } from "@sourceacademy/common-stepper"; import { expect, test } from "vitest"; -import { RUNNER_ID as STEPPER_RUNNER_ID, STEPPER_CHANNEL_ID } from "@sourceacademy/common-stepper"; import { + ENV_BLOCK_NODE_TYPE, E_STEPPER_CHANNEL_ID, E_STEPPER_DIRECTORY_ID, - ENV_BLOCK_NODE_TYPE, REF_NODE_TYPE, RUNNER_ID, WEB_ID, diff --git a/src/common/e-stepper/tsconfig.json b/src/common/e-stepper/tsconfig.json index 88a8445..c7482f9 100644 --- a/src/common/e-stepper/tsconfig.json +++ b/src/common/e-stepper/tsconfig.json @@ -5,7 +5,6 @@ "compilerOptions": { "declaration": true, "outDir": "./dist", - "rootDir": "./src", - "types": ["jest"] + "rootDir": "./src" } } diff --git a/src/runner/e-stepper/jest.config.cjs b/src/runner/e-stepper/jest.config.cjs deleted file mode 100644 index 06bb4a9..0000000 --- a/src/runner/e-stepper/jest.config.cjs +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - preset: "ts-jest/presets/js-with-ts-esm", - testEnvironment: "node", - transform: { - "^.+\\.tsx?$": [ - "ts-jest", - { - useESM: true, - }, - ], - }, - testPathIgnorePatterns: [".*?dist/"], - coverageReporters: ["lcov"], -}; diff --git a/src/runner/e-stepper/package.json b/src/runner/e-stepper/package.json index 12f0d91..2818dc6 100644 --- a/src/runner/e-stepper/package.json +++ b/src/runner/e-stepper/package.json @@ -30,18 +30,16 @@ "@sourceacademy/runner-stepper": ">=0.0.1" }, "devDependencies": { - "@rollup/plugin-node-resolve": "^16.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-node-resolve": "catalog:", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@sourceacademy/common-e-stepper": "workspace:*", "@sourceacademy/common-stepper": "workspace:*", - "@sourceacademy/conductor": "^0.8.0", + "@sourceacademy/conductor": "catalog:", "@sourceacademy/runner-stepper": "workspace:*", - "@types/jest": "^30.0.0", - "jest": "^30.4.2", - "rollup": "^4.60.2", - "ts-jest": "^29.4.11", - "tslib": "^2.8.1", - "typescript": "^6.0.3" + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/src/runner/e-stepper/rollup.config.mjs b/src/runner/e-stepper/rollup.config.mjs index 238a07c..05aaa22 100644 --- a/src/runner/e-stepper/rollup.config.mjs +++ b/src/runner/e-stepper/rollup.config.mjs @@ -1,11 +1,11 @@ +// @ts-check + import nodeResolve from "@rollup/plugin-node-resolve"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from "rollup"; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: [ { @@ -17,5 +17,9 @@ export default { format: "esm", }, ], - plugins: [nodeResolve(), typescript(), terser()], -}; + plugins: [ + nodeResolve(), + typescript({ exclude: ['**/__tests__/**'] }), + terser() + ], +}); diff --git a/src/runner/e-stepper/src/__tests__/runner.test.ts b/src/runner/e-stepper/src/__tests__/runner.test.ts index 7296530..62cb743 100644 --- a/src/runner/e-stepper/src/__tests__/runner.test.ts +++ b/src/runner/e-stepper/src/__tests__/runner.test.ts @@ -1,4 +1,3 @@ -import { expect, test } from "vitest"; import { E_STEPPER_CHANNEL_ID, @@ -7,6 +6,7 @@ import { } from "@sourceacademy/common-e-stepper"; import { STEPPER_CHANNEL_ID } from "@sourceacademy/common-stepper"; import { BaseStepperRunnerPlugin } from "@sourceacademy/runner-stepper"; +import { expect, test } from "vitest"; import { BaseEStepperRunnerPlugin } from ".."; class FakeChannel { @@ -43,7 +43,7 @@ class CountingEStepper extends BaseEStepperRunnerPlugin { } const make = (Cls: new (...args: never[]) => T, channel: FakeChannel): T => - // eslint-disable-next-line @typescript-eslint/no-explicit-any + new (Cls as any)({} as any, [channel as any]); test("attaches to the e-stepper channel, not the stepper's", () => { diff --git a/src/runner/e-stepper/tsconfig.json b/src/runner/e-stepper/tsconfig.json index 88a8445..c7482f9 100644 --- a/src/runner/e-stepper/tsconfig.json +++ b/src/runner/e-stepper/tsconfig.json @@ -5,7 +5,6 @@ "compilerOptions": { "declaration": true, "outDir": "./dist", - "rootDir": "./src", - "types": ["jest"] + "rootDir": "./src" } } diff --git a/src/runner/test/tsconfig.json b/src/runner/test/tsconfig.json index c7482f9..4b54a89 100644 --- a/src/runner/test/tsconfig.json +++ b/src/runner/test/tsconfig.json @@ -5,6 +5,8 @@ "compilerOptions": { "declaration": true, "outDir": "./dist", - "rootDir": "./src" + "rootDir": "./src", + // Some weird stuff is happening with py-slang + "skipLibCheck": true } } diff --git a/src/web/data-visualizer/src/__tests__/treeViews.test.ts b/src/web/data-visualizer/src/__tests__/treeViews.test.ts index e1bd242..fce4ba8 100644 --- a/src/web/data-visualizer/src/__tests__/treeViews.test.ts +++ b/src/web/data-visualizer/src/__tests__/treeViews.test.ts @@ -2,13 +2,14 @@ import type { SerializedDataVisualizerNode } from '@sourceacademy/common-data-vi import { Text } from 'react-konva'; import { describe, expect, test, vi } from 'vitest'; +import { Config } from '../Config'; import type { ClassificationResult } from '../classify'; import { ArrowDrawable, BackwardArrowDrawable } from '../drawable/Drawable'; import { AlreadyParsedTreeNode } from '../tree/AlreadyParsedTreeNode'; import { BinaryTreeDrawer } from '../tree/BinaryTreeDrawer'; import { GeneralTreeDrawer } from '../tree/GeneralTreeDrawer'; import { Tree } from '../tree/Tree'; -import { ArrayTreeNode, FunctionTreeNode } from '../tree/TreeNode'; +import { ArrayTreeNode, DataTreeNode, FunctionTreeNode } from '../tree/TreeNode'; // Same rationale as tree.test.ts: avoid Konva's Node-environment build requiring the optional // `canvas` package for what are, here, pure layout-arithmetic and warning-fallback checks. @@ -47,22 +48,7 @@ vi.mock(import('react-konva'), () => { } as any; }); -<<<<<<< HEAD -const empty = (): SerializedDataVisualizerNode => ({ type: 'empty' }); -======= -import { Text } from "react-konva"; - -import type { ClassificationResult } from "../classify"; -import { Config } from "../Config"; -import { ArrowDrawable, BackwardArrowDrawable } from "../drawable/Drawable"; -import { AlreadyParsedTreeNode } from "../tree/AlreadyParsedTreeNode"; -import { BinaryTreeDrawer } from "../tree/BinaryTreeDrawer"; -import { GeneralTreeDrawer } from "../tree/GeneralTreeDrawer"; -import { Tree } from "../tree/Tree"; -import { ArrayTreeNode, DataTreeNode, FunctionTreeNode } from "../tree/TreeNode"; - const empty = (): SerializedDataVisualizerNode => ({ type: "empty" }); ->>>>>>> origin/main const leaf = (n: number): SerializedDataVisualizerNode => ({ type: 'leaf', displayValue: String(n), @@ -164,16 +150,12 @@ describe(BinaryTreeDrawer, () => { expect(deep.height).toBeGreaterThan(single.height); }); -<<<<<<< HEAD - test('the bare empty terminator is a trivial valid binary tree, drawn via the measured-text branch', () => { -======= test("an empty value carries the language's spelling of it through to the root node", () => { const tree = Tree.fromSerializedNode({ type: "empty", displayValue: "None" }); expect((tree.rootNode as DataTreeNode).displayValue).toBe("None"); }); test("the bare empty terminator is a trivial valid binary tree, drawn via the measured-text branch", () => { ->>>>>>> origin/main // Per isBinaryTreeNode: a binary tree is `null`, or a 3-element list — a bare *leaf* value is // neither, so (unlike this case) it fails classification and hits the warning box instead. const tree = Tree.fromSerializedNode(empty()); @@ -237,10 +219,6 @@ describe(BinaryTreeDrawer, () => { }); }); -<<<<<<< HEAD -describe(GeneralTreeDrawer, () => { - test('a non-general-tree structure draws the fixed-size warning box instead of a tree', () => { -======= describe("BinaryTreeDrawer canvas (issue #84)", () => { // Python value -> wire node, the way py-slang serializes it (2-element list -> pair). type Py = null | number | string | Py[]; @@ -340,7 +318,7 @@ describe("BinaryTreeDrawer canvas margin", () => { ); }); -describe("GeneralTreeDrawer", () => { +describe(GeneralTreeDrawer, () => { test("draws a list of functions as a tree with three data items, not the warning box (issue #113)", () => { // draw_data(llist(lambda x : x, lambda y: y, lambda z: z)) const fn = (): SerializedDataVisualizerNode => ({ @@ -427,7 +405,6 @@ describe("GeneralTreeDrawer", () => { ); test("a non-general-tree structure draws the fixed-size warning box instead of a tree", () => { ->>>>>>> origin/main // An *improper* list (here, a bare 2-tuple whose second slot is a leaf rather than another pair // or the empty terminator) is the only shape General Tree View actually rejects — a binary-tree- // shaped input is itself a valid (proper-list) general tree, see classify.test.ts. diff --git a/src/web/e-stepper/package.json b/src/web/e-stepper/package.json index 7cae501..321971d 100644 --- a/src/web/e-stepper/package.json +++ b/src/web/e-stepper/package.json @@ -28,16 +28,16 @@ "devDependencies": { "@blueprintjs/core": "^6.0.0", "@blueprintjs/icons": "^6.0.0", - "@rollup/plugin-commonjs": "^29.0.0", - "@rollup/plugin-node-resolve": "^16.0.3", + "@rollup/plugin-commonjs": "catalog:", + "@rollup/plugin-node-resolve": "catalog:", "@rollup/plugin-replace": "^6.0.3", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.3.0", + "@rollup/plugin-terser": "catalog:", + "@rollup/plugin-typescript": "catalog:", "@sourceacademy/common-cse-machine": "workspace:*", "@sourceacademy/common-e-stepper": "workspace:*", "@sourceacademy/common-stepper": "workspace:*", "@sourceacademy/common-tabs": "workspace:^", - "@sourceacademy/conductor": "^0.8.0", + "@sourceacademy/conductor": "catalog:", "@types/react": "^19.0.0", "@types/react-test-renderer": "^19.0.0", "classnames": "^2.3.2", @@ -46,8 +46,9 @@ "react-dom": "^19.0.0", "react-konva": "^19.0.0", "react-test-renderer": "^19.0.0", - "rollup": "^4.60.2", - "tslib": "^2.8.1", - "typescript": "^6.0.3" + "rollup": "catalog:", + "tslib": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/src/web/e-stepper/rollup.config.mjs b/src/web/e-stepper/rollup.config.mjs index 2322423..d8c56c2 100644 --- a/src/web/e-stepper/rollup.config.mjs +++ b/src/web/e-stepper/rollup.config.mjs @@ -1,8 +1,11 @@ +// @ts-check + import commonjs from "@rollup/plugin-commonjs"; import nodeResolve from "@rollup/plugin-node-resolve"; import replace from "@rollup/plugin-replace"; import terser from "@rollup/plugin-terser"; import typescript from "@rollup/plugin-typescript"; +import { defineConfig } from "rollup"; // React, Blueprint, and Konva/react-konva are provided by the host at load time through the // require-provider (see the frontend's `requireProvider`, which already exposes them for other @@ -21,10 +24,7 @@ const external = [ "react-konva", ]; -/** - * @type {import('rollup').RollupOptions} - */ -export default { +export default defineConfig({ input: "src/index.ts", output: { file: "dist/index.cjs", @@ -38,7 +38,7 @@ export default { replace({ preventAssignment: true, "process.env.NODE_ENV": JSON.stringify("production") }), nodeResolve({ browser: true, preferBuiltins: false }), commonjs(), - typescript(), + typescript({ exclude: ['**/__tests__/**'] }), terser(), ], -}; +}); diff --git a/src/web/e-stepper/src/EStepperHostPlugin.tsx b/src/web/e-stepper/src/EStepperHostPlugin.tsx index ebc76a2..83827b0 100644 --- a/src/web/e-stepper/src/EStepperHostPlugin.tsx +++ b/src/web/e-stepper/src/EStepperHostPlugin.tsx @@ -1,3 +1,4 @@ +import type { ICseDiagramService, IHostServices } from "@sourceacademy/common-cse-machine"; import { E_STEPPER_CHANNEL_ID, WEB_ID, @@ -5,7 +6,6 @@ import { type EStepperStep, type SyntaxProfile, } from "@sourceacademy/common-e-stepper"; -import type { ICseDiagramService, IHostServices } from "@sourceacademy/common-cse-machine"; import type { ITabService, Tab } from "@sourceacademy/common-tabs"; import { checkIsPluginClass, diff --git a/src/web/e-stepper/src/EStepperView.tsx b/src/web/e-stepper/src/EStepperView.tsx index 26a8c6d..b2499d0 100644 --- a/src/web/e-stepper/src/EStepperView.tsx +++ b/src/web/e-stepper/src/EStepperView.tsx @@ -25,9 +25,9 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { CustomASTRenderer, type NodeRenderers, type StepperNode } from "../../stepper/src/render"; import { sliderLabels } from "../../stepper/src/sliderLabels"; import { injectStepperStyles } from "../../stepper/src/styles"; -import { frameColor } from "./colors"; import EnvDiagram from "./EnvDiagram"; import ProgramArrows, { ENV_ATTRIBUTE, REF_ATTRIBUTE } from "./ProgramArrows"; +import { frameColor } from "./colors"; import { injectEStepperStyles } from "./styles"; const DEFAULT_PROGRAM_HEIGHT = 240; diff --git a/src/web/e-stepper/src/EnvDiagram.tsx b/src/web/e-stepper/src/EnvDiagram.tsx index 735bc9a..9cc7e5b 100644 --- a/src/web/e-stepper/src/EnvDiagram.tsx +++ b/src/web/e-stepper/src/EnvDiagram.tsx @@ -10,12 +10,12 @@ import { Arrow, Circle, Group, Layer, Line, Rect, Stage, Text } from "react-konv import { DiagramColors, frameColor } from "./colors"; import { + LayoutConfig as C, + layoutDiagram, + valueLabel, type ArrowSpec, type FrameBox, - layoutDiagram, - LayoutConfig as C, type ObjectBox, - valueLabel, } from "./layout"; const FONT = "Inconsolata, Consolas, monospace"; diff --git a/src/web/e-stepper/src/__tests__/EStepperHostPlugin.test.tsx b/src/web/e-stepper/src/__tests__/EStepperHostPlugin.test.tsx index a6c3551..a7d86d7 100644 --- a/src/web/e-stepper/src/__tests__/EStepperHostPlugin.test.tsx +++ b/src/web/e-stepper/src/__tests__/EStepperHostPlugin.test.tsx @@ -3,8 +3,11 @@ import { act, createElement } from "react"; import TestRenderer from "react-test-renderer"; import { describe, expect, test, vi } from "vitest"; -vi.mock("konva", () => ({ default: {} })); -vi.mock("react-konva", () => { +import { EStepperHostPlugin } from "../EStepperHostPlugin"; +import steps from "./makeWithdrawSteps.json"; + +vi.mock(import('konva'), () => ({ default: {} as any })); +vi.mock(import('react-konva'), () => { const stub = (name: string) => (props: Record) => createElement(`konva-${name.toLowerCase()}`, props, props.children as never); return Object.fromEntries( @@ -13,14 +16,11 @@ vi.mock("react-konva", () => { }); // Blueprint's overlays need a DOM to unmount (the options menu goes when the host draws the diagram). -vi.mock("@blueprintjs/core", async (importOriginal: () => Promise) => ({ +vi.mock(import('@blueprintjs/core'), async importOriginal => ({ ...(await importOriginal()), - Popover: (props: Record) => createElement("popover-stub", props), + Popover: ((props: Record) => createElement("popover-stub", props)) as any })); -import { EStepperHostPlugin } from "../EStepperHostPlugin"; -import steps from "./makeWithdrawSteps.json"; - type PluginArgs = ConstructorParameters; function setUp(hostServices?: PluginArgs[3]) { @@ -38,11 +38,11 @@ function setUp(hostServices?: PluginArgs[3]) { // Stubs with only what the plugin uses. const conduit = {} as PluginArgs[0]; const tabServiceStub = tabService as unknown as PluginArgs[2]; - const plugin = new EStepperHostPlugin(conduit, [channel as never], tabServiceStub, hostServices); + const plugin = new EStepperHostPlugin(conduit, [channel], tabServiceStub, hostServices); return { plugin, deliver: (m: EStepperMessage) => act(() => deliver(m)), sent, tab, tabService }; } -describe("EStepperHostPlugin", () => { +describe(EStepperHostPlugin, () => { test("registers and reveals the E-Stepper tab, and asks for the last run's steps", () => { const { tab, tabService, sent } = setUp(); expect(tab).toMatchObject({ id: "e-stepper", label: "E-Stepper", iconName: "flow-review" }); diff --git a/src/web/e-stepper/src/__tests__/EStepperView.test.tsx b/src/web/e-stepper/src/__tests__/EStepperView.test.tsx index 4bbfde5..f4cbd8a 100644 --- a/src/web/e-stepper/src/__tests__/EStepperView.test.tsx +++ b/src/web/e-stepper/src/__tests__/EStepperView.test.tsx @@ -1,13 +1,18 @@ +import { Button, Checkbox, Popover, Slider, Switch } from "@blueprintjs/core"; import type { CseDiagramViewProps } from "@sourceacademy/common-cse-machine"; import type { EStepperStep, SyntaxProfile } from "@sourceacademy/common-e-stepper"; import { act, createElement } from "react"; -import { Button, Checkbox, Popover, Slider, Switch } from "@blueprintjs/core"; import TestRenderer from "react-test-renderer"; import { beforeEach, describe, expect, test, vi } from "vitest"; +import EStepperView, { nextBreakpoint, previousBreakpoint, sliderLabels } from "../EStepperView"; +import ProgramArrows from "../ProgramArrows"; +import steps from "./makeWithdrawSteps.json"; +import pythonProfile from "./pythonProfile.json"; + // As in the data visualizer's tests: avoid Konva's Node build (it requires the optional `canvas`). -vi.mock("konva", () => ({ default: {} })); -vi.mock("react-konva", () => { +vi.mock(import('konva'), () => ({ default: {} as any })); +vi.mock(import('react-konva'), () => { const stub = (name: string) => (props: Record) => createElement(`konva-${name.toLowerCase()}`, props, props.children as never); return Object.fromEntries( @@ -16,18 +21,13 @@ vi.mock("react-konva", () => { }); // Blueprint's overlays need a DOM; the tests read the options menu's content from the props. -vi.mock("@blueprintjs/core", async (importOriginal: () => Promise) => ({ +vi.mock(import('@blueprintjs/core'), async (importOriginal: () => Promise) => ({ ...(await importOriginal()), - Popover: (props: Record) => createElement("popover-stub", props), + Popover: ((props: Record) => createElement("popover-stub", props)) as any })); -import EStepperView, { nextBreakpoint, previousBreakpoint, sliderLabels } from "../EStepperView"; -import ProgramArrows from "../ProgramArrows"; -import steps from "./makeWithdrawSteps.json"; - /** A stubbed Konva shape's host element type (see the react-konva mock above). */ const konva = (type: string) => type as unknown as React.ElementType; -import pythonProfile from "./pythonProfile.json"; // Both produced by py-slang's e-stepper: three steps of make_withdraw, and its Python profile. const fixture = steps as unknown as EStepperStep[]; @@ -80,7 +80,7 @@ const hasClass = (node: TestRenderer.ReactTestInstance, name: string): boolean = const text = (node: TestRenderer.ReactTestInstance): string => node.children.map(c => (typeof c === "string" ? c : text(c))).join(""); -describe("EStepperView", () => { +describe(EStepperView, () => { // The slider remounts when the tab's size is known (it measures its track then), and a Blueprint // slider takes its listeners off the document when it goes away. beforeEach(() => { diff --git a/src/web/e-stepper/src/__tests__/EnvDiagram.test.tsx b/src/web/e-stepper/src/__tests__/EnvDiagram.test.tsx index fea1775..4ee2948 100644 --- a/src/web/e-stepper/src/__tests__/EnvDiagram.test.tsx +++ b/src/web/e-stepper/src/__tests__/EnvDiagram.test.tsx @@ -3,11 +3,16 @@ import { act, createElement } from "react"; import TestRenderer from "react-test-renderer"; import { describe, expect, test, vi } from "vitest"; +import EnvDiagram from "../EnvDiagram"; +import { DiagramColors } from "../colors"; +import { layoutDiagram } from "../layout"; +import steps from "./makeWithdrawSteps.json"; + // Konva's Node build needs the optional `canvas` package, so the shapes are stubbed — as plain // host elements (`konva-rect`, ...) that keep their props and render their children, so the // drawing code runs and what it draws can be inspected. -vi.mock("konva", () => ({ default: {} })); -vi.mock("react-konva", () => { +vi.mock(import('konva'), () => ({ default: {} as any })); +vi.mock(import('react-konva'), () => { const stub = (name: string) => (props: Record) => createElement(`konva-${name.toLowerCase()}`, props, props.children as never); return Object.fromEntries( @@ -15,11 +20,6 @@ vi.mock("react-konva", () => { ); }); -import { DiagramColors } from "../colors"; -import EnvDiagram from "../EnvDiagram"; -import { layoutDiagram } from "../layout"; -import steps from "./makeWithdrawSteps.json"; - /** A stubbed Konva shape's host element type (see the react-konva mock above). */ const konva = (type: string) => type as unknown as React.ElementType; @@ -50,7 +50,7 @@ function draw(overrides: Partial[0]> = {}) { const texts = (root: TestRenderer.ReactTestInstance) => root.findAllByType(konva("konva-text")).map(t => String(t.props.text)); -describe("EnvDiagram", () => { +describe(EnvDiagram, () => { test("draws every frame with its label and bindings", () => { const all = texts(draw().root); expect(all).toEqual(expect.arrayContaining(["Global", "E1 make_withdraw", "E2 withdraw"])); diff --git a/src/web/e-stepper/src/__tests__/ProgramArrows.test.tsx b/src/web/e-stepper/src/__tests__/ProgramArrows.test.tsx index 0d1520a..53f520a 100644 --- a/src/web/e-stepper/src/__tests__/ProgramArrows.test.tsx +++ b/src/web/e-stepper/src/__tests__/ProgramArrows.test.tsx @@ -3,10 +3,10 @@ import TestRenderer from "react-test-renderer"; import { describe, expect, test, vi } from "vitest"; import ProgramArrows, { - arrowGeometry, - computeProgramArrows, ENV_ATTRIBUTE, REF_ATTRIBUTE, + arrowGeometry, + computeProgramArrows, } from "../ProgramArrows"; const rect = (left: number, top: number, width: number, height: number) => @@ -24,7 +24,7 @@ function fake(box: DOMRect, attributes: Record = {}, marks: unkn } as unknown as Element; } -describe("arrowGeometry", () => { +describe(arrowGeometry, () => { test("leaves and arrives horizontally when the target is further across than down", () => { const { path, head } = arrowGeometry({ x: 0, y: 0 }, { x: 100, y: 20 }); expect(path).toBe("M 0 0 C 50 0, 50 20, 100 20"); @@ -76,7 +76,7 @@ describe("arrowGeometry scale", () => { }); }); -describe("computeProgramArrows", () => { +describe(computeProgramArrows, () => { const container = fake(rect(100, 50, 800, 400)); const program = (marks: unknown[]) => fake(rect(100, 50, 300, 400), {}, marks); const diagram = fake(rect(500, 50, 400, 400)); @@ -114,7 +114,7 @@ describe("computeProgramArrows", () => { }); }); -describe("ProgramArrows", () => { +describe(ProgramArrows, () => { const ref = fake(rect(150, 100, 20, 10), { [REF_ATTRIBUTE]: "#1" }); const env = fake(rect(120, 200, 20, 12), { [ENV_ATTRIBUTE]: "E1" }); const listeners = new Map void>(); diff --git a/src/web/e-stepper/src/__tests__/layout.test.ts b/src/web/e-stepper/src/__tests__/layout.test.ts index 40b30fc..a28f271 100644 --- a/src/web/e-stepper/src/__tests__/layout.test.ts +++ b/src/web/e-stepper/src/__tests__/layout.test.ts @@ -6,8 +6,8 @@ import type { import { describe, expect, test } from "vitest"; import { frameColor } from "../colors"; -import { layoutDiagram, LayoutConfig as C, type DiagramLayout } from "../layout"; -import steps from "./makeWithdrawSteps.json"; +import { LayoutConfig as C, layoutDiagram, type DiagramLayout } from "../layout"; +import steps from "./makeWithdrawSteps.json" with { type: 'json' }; /** The last step of `make_withdraw` (produced by py-slang's e-stepper engine): frames Global, E1, * E2 (garbage), function objects #1 (in Global) and #2 (in E1), lists #4 = [W1, #3] and #3. */ diff --git a/src/web/stepper/src/SubstVisualizer.tsx b/src/web/stepper/src/SubstVisualizer.tsx index 3b866ca..e0a5dd0 100644 --- a/src/web/stepper/src/SubstVisualizer.tsx +++ b/src/web/stepper/src/SubstVisualizer.tsx @@ -1,36 +1,3 @@ -<<<<<<< HEAD -import { - Button, - ButtonGroup, - Card, - Classes, - Divider, - Icon, - Popover, - Pre, - Slider, -} from '@blueprintjs/core'; -import { getHotkeyHandler, type HotkeyItem } from '@mantine/hooks'; -import type { - FunctionValueRule, - SerializedStepperNode, - SerializedStepperStep, - SyntaxProfile, - SyntaxTemplatePart, -} from '@sourceacademy/common-stepper'; -import classNames from 'classnames'; -import { useCallback, useEffect, useState } from 'react'; - -import { injectStepperStyles } from './styles'; - -/** - * The serialized AST nodes are plain JSON. `Record` lets the renderer read the - * language-specific fields (e.g. `left`, `operator`, `params`) without per-node typing, exactly as - * the original (class-based) renderer did after casting. - */ -type StepperNode = SerializedStepperNode & Record; - -======= import { Button, ButtonGroup, Card, Classes, Divider, Pre, Slider } from "@blueprintjs/core"; import { getHotkeyHandler, type HotkeyItem } from "@mantine/hooks"; import type { SerializedStepperStep, SyntaxProfile } from "@sourceacademy/common-stepper"; @@ -41,7 +8,6 @@ import { CustomASTRenderer } from "./render"; import { sliderLabels } from "./sliderLabels"; import { injectStepperStyles } from "./styles"; ->>>>>>> origin/main function SubstDefaultText() { return (
@@ -234,12 +200,8 @@ export default function StepperView(props: StepperViewProps) { return (
>>>>>> origin/main onKeyDown={hotkeyHandler} tabIndex={-1} // tab index necessary to fire keydown events on div element > @@ -279,960 +241,3 @@ export default function StepperView(props: StepperViewProps) {
); } -<<<<<<< HEAD - -/* - Custom AST renderer for Stepper (Inspired by astring library) - This custom AST renderer utilizes the recursive approach of handling rendering of various - StepperNodes by using nested
and . Unlike React-ace, using our own renderer makes our - stepper more customizable. For example, we can add a code component that is hoverable using a - blueprint tooltip. -*/ - -interface RenderContext { - parentNode?: StepperNode; - isRight?: boolean; // specified for binary expression - styleWrapper: StyleWrapper; - popoverDepth?: number; - /** - * The active language's rendering rules. When present, nodes are rendered generically from their - * template (see {@link renderNode}); when absent, the built-in Source/JavaScript renderers are - * used. Threaded so a whole tree renders in one language. - */ - profile?: SyntaxProfile; - /** - * Forces a named function value to render its full body (its template) rather than collapsing to a - * mu-term. Set only when rendering the contents of a function-definition popover, so the popover - * shows the body while every other occurrence stays collapsed. See {@link SyntaxProfile.functionValues}. - */ - expandFunctionValue?: boolean; -} - -/** Maps a profile token class to the stepper's CSS colour class. */ -const TOKEN_CLASS: Record = { - operator: 'stepper-operator', - identifier: 'stepper-identifier', - literal: 'stepper-literal', - conditional: 'stepper-conditional-operator', -}; - -/** Reads a node property by a (possibly dotted, e.g. `"id.name"`) path, for profile `prop` parts. */ -function readNodeProp(node: StepperNode, path: string): unknown { - return path - .split('.') - .reduce( - (value, key) => (value == null ? value : (value as Record)[key]), - node, - ); -} - -type StyleWrapper = (node: StepperNode) => (preformatted: React.ReactNode) => React.ReactNode; - -// composeStyleWrapper takes two style wrappers and merges their effect together. -function composeStyleWrapper( - first: StyleWrapper | undefined, - second: StyleWrapper | undefined, -): StyleWrapper | undefined { - return first === undefined && second === undefined - ? undefined - : first === undefined - ? second - : second === undefined - ? first - : (node: StepperNode) => (preformatted: React.ReactNode) => { - const afterFirstStyle = first(node)(preformatted); - return second(node)(afterFirstStyle); - }; -} - -interface FunctionDefinitionPopoverContentProps { - node: StepperNode; - styleWrapper: StyleWrapper | undefined; - popoverDepth: number; - renderNode: (node: StepperNode | undefined | null, context: RenderContext) => React.ReactNode; - renderFunctionArguments: ( - nodes: StepperNode[], - renderNodeFn: (node: StepperNode | undefined | null, context: RenderContext) => React.ReactNode, - styleWrapper: StyleWrapper | undefined, - popoverDepth: number, - ) => React.ReactNode; -} - -function FunctionDefinitionPopoverContent({ - node, - styleWrapper, - popoverDepth, - renderNode, - renderFunctionArguments, -}: FunctionDefinitionPopoverContentProps) { - return ( -
-
- - {' Function definition'} -
-          
-            {renderFunctionArguments(node.params, renderNode, styleWrapper, popoverDepth)}
-            {' => '}
-            {renderNode(node.body, {
-              styleWrapper: styleWrapper ?? (_node => p => p),
-              popoverDepth: popoverDepth + 1,
-            })}
-          
-        
-
-
- ); -} - -interface ProfileFunctionDefinitionPopoverProps { - node: StepperNode; - wrapper: StyleWrapper | undefined; - popoverDepth: number; - profile?: SyntaxProfile; -} - -/** - * The popover body for a profile-rendered (e.g. Python) function value: the function's full - * definition, rendered from its own template (forced-expanded), inside the same chrome the built-in - * popover uses. This is a **component** (not an eagerly-computed node) so React renders it lazily — - * only when the popover actually opens — which keeps a *recursive* function's nested popovers from - * expanding forever at render time (each level renders on hover, exactly like the built-in popover). - */ -function ProfileFunctionDefinitionPopover({ - node, - wrapper, - popoverDepth, - profile, -}: ProfileFunctionDefinitionPopoverProps) { - return ( -
-
- - {' Function definition'} -
-          
-            {renderNode(node, {
-              styleWrapper: wrapper ?? (_n => p => p),
-              popoverDepth: popoverDepth + 1,
-              profile,
-              expandFunctionValue: true,
-            })}
-          
-        
-
-
- ); -} - -/** - * The popover body for a {@link HoverTextRule}: a single already-formatted line the language - * computed ahead of time (e.g. `"built-in function print"`), unlike - * {@link ProfileFunctionDefinitionPopover}'s expanded function body — there is no body to render here, - * just the text, inside the same chrome the function-definition popover uses. - */ -function ProfileHoverTextPopover({ text }: { text: string }) { - return ( -
-
- - {` ${text}`} -
-
- ); -} - -/** Popover body for an `image` part: the inline opaque-value thumbnail, enlarged to be legible. */ -function EnlargedThumbnailPopover({ src, alt }: { src: string; alt?: string }) { - return ( -
-
- {alt ? ( - <> - - {` ${alt}`} - - ) : null} - {alt -
-
- ); -} - -/** - * renderNode renders a serialized Stepper AST node to a React ReactNode. - */ -function renderNode( - currentNode: StepperNode | null | undefined, - renderContext: RenderContext, -): React.ReactNode { - if (currentNode == null) return null; - const styleWrapper = renderContext.styleWrapper; - const popoverDepth = renderContext.popoverDepth ?? 0; - const renderers = { - Literal(node: StepperNode) { - const stringifyLiteralValue = (value: unknown) => - typeof value === 'string' ? '"' + value + '"' : value !== null ? String(value) : 'null'; - return ( - - {node.raw ? node.raw : stringifyLiteralValue(node.value)} - - ); - }, - Identifier(node: StepperNode) { - return {node.name}; - }, - // Expressions - UnaryExpression(node: StepperNode) { - return ( - - {`${node.operator}`} - {renderNode(node.argument, { - parentNode: node, - styleWrapper: styleWrapper, - popoverDepth: popoverDepth, - })} - - ); - }, - BinaryExpression(node: StepperNode) { - return ( - - {renderNode(node.left, { - parentNode: node, - isRight: false, - styleWrapper: styleWrapper, - popoverDepth: popoverDepth, - })} - {` ${node.operator} `} - {renderNode(node.right, { - parentNode: node, - isRight: true, - styleWrapper: styleWrapper, - popoverDepth: popoverDepth, - })} - - ); - }, - LogicalExpression(node: StepperNode) { - return ( - - {renderNode(node.left, { - parentNode: node, - isRight: false, - styleWrapper: styleWrapper, - popoverDepth: popoverDepth, - })} - {` ${node.operator} `} - {renderNode(node.right, { - parentNode: node, - isRight: true, - styleWrapper: styleWrapper, - popoverDepth: popoverDepth, - })} - - ); - }, - ConditionalExpression(node: StepperNode) { - return ( - - {renderNode(node.test, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} - {` ? `} - {renderNode(node.consequent, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} - {` : `} - {renderNode(node.alternate, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} - - ); - }, - ArrayExpression(node: StepperNode) { - // Render all arguments inside an array - const args: React.ReactNode[] = node.elements - .filter((arg: StepperNode | null) => arg !== null) - .map((arg: StepperNode) => - renderNode(arg, { styleWrapper: styleWrapper, popoverDepth: popoverDepth }), - ); - - const renderedArguments = args.slice(1).reduce( - (result, item) => ( - - {result} - {', '} - {item} - - ), - args[0], - ); - return ( - - {'['} - {renderedArguments} - {']'} - - ); - }, - ArrowFunctionExpression(node: StepperNode) { - /** - * Add hovering effect to children nodes only if it is an identifier with the name - * corresponding to the name of lambda expression - */ - function muTermStyleWrapper(targetNode: StepperNode) { - if (targetNode.type === 'Identifier' && targetNode.name === node.name) { - function addHovering(preprocessed: React.ReactNode): React.ReactNode { - return ( - - - } - > - {preprocessed} - - - ); - } - return addHovering; - } else { - // Do nothing - return (preprocessed: React.ReactNode) => preprocessed; - } - } - - // If the name is specified, render the name and add hovering for the body. - return node.name ? ( - - - } - > - {node.name} - - - ) : ( - - {renderFunctionArguments(node.params, renderNode, styleWrapper, popoverDepth)} - {' => '} - {renderNode(node.body, { - styleWrapper: composeStyleWrapper(styleWrapper, muTermStyleWrapper)!, - popoverDepth: popoverDepth, - })} - - ); - }, - CallExpression(node: StepperNode) { - let renderedCallee = renderNode(node.callee, { - styleWrapper: styleWrapper, - popoverDepth: popoverDepth, - }); - if (node.callee.type === 'ArrowFunctionExpression' && node.callee.name === undefined) { - renderedCallee = ( - - {'('} - {renderedCallee} - {')'} - - ); - } - return ( - - {renderedCallee} - {renderArguments(node.arguments)} - - ); - }, - Program(node: StepperNode) { - return ( - - {node.body.map((ast: StepperNode, index: number) => ( -
- {renderNode(ast, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} -
- ))} -
- ); - }, - IfStatement(node: StepperNode) { - return ( - - - {'if '} - {'('} - - {renderNode(node.test, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} - - {') '} - - - {renderNode(node.consequent, { - styleWrapper: styleWrapper, - popoverDepth: popoverDepth, - })} - - {node.alternate && ( - - {' else '} - {renderNode(node.alternate, { - styleWrapper: styleWrapper, - popoverDepth: popoverDepth, - })} - - )} - - ); - }, - ReturnStatement(node: StepperNode) { - return ( - - {'return '} - {node.argument && - renderNode(node.argument, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} - {';'} - - ); - }, - BlockStatement(node: StepperNode) { - return ( - - {'{'} - {node.body.map((ast: StepperNode, index: number) => ( -
- {renderNode(ast, { styleWrapper, popoverDepth: popoverDepth })} -
- ))} - {'}'} -
- ); - }, - ExpressionStatement(node: StepperNode) { - return ( - - {renderNode(node.expression, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} - {';'} - - ); - }, - FunctionDeclaration(node: StepperNode) { - return ( - - {`function ${node.id.name}`} - {renderArguments(node.params)} - - {' '} - {renderNode(node.body, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} - - - ); - }, - VariableDeclaration(node: StepperNode) { - return ( - - {node.kind} - {node.declarations.map((ast: StepperNode, idx: number) => ( - - {idx !== 0 && ', '} - {renderNode(ast, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} - - ))} - {';'} - - ); - }, - VariableDeclarator(node: StepperNode) { - return ( - - {renderNode(node.id, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} - {' = '} - {node.init - ? renderNode(node.init, { styleWrapper: styleWrapper, popoverDepth: popoverDepth }) - : 'undefined'} - - ); - }, - DebuggerStatement(_node: StepperNode) { - return debugger;; - }, - }; - - // Additional renderers - const renderFunctionArguments = ( - nodes: StepperNode[] | undefined, - renderNodeFn: typeof renderNode, - styleWrapper: StyleWrapper | undefined, - popoverDepth: number, - ) => { - if (!nodes) return '()'; - const args: React.ReactNode[] = nodes.map(arg => - renderNodeFn(arg, { - styleWrapper: styleWrapper ?? (_node => p => p), - popoverDepth: popoverDepth, - }), - ); - let renderedArguments = args.slice(1).reduce( - (result, item) => ( - - {result} - {', '} - {item} - - ), - args[0], - ); - if (args.length !== 1) { - renderedArguments = ( - - {'('} - {renderedArguments} - {')'} - - ); - } - return renderedArguments; - }; - - const renderArguments = (nodes: StepperNode[] | undefined) => { - if (!nodes) return '()'; - const args: React.ReactNode[] = nodes.map(arg => - renderNode(arg, { styleWrapper: styleWrapper, popoverDepth: popoverDepth }), - ); - let renderedArguments = args.slice(1).reduce( - (result, item) => ( - - {result} - {', '} - {item} - - ), - args[0], - ); - renderedArguments = ( - - {'('} - {renderedArguments} - {')'} - - ); - return renderedArguments; - }; - - // Renders a node generically from a language profile's template (see SyntaxProfile). The host - // knows no grammar: each part emits literal text or recurses into a child (itself rendered via the - // profile), so any language renders with zero host-side, language-specific code. Only `child` - // parts establish a parenthesisation context; list/block/line items intentionally do not. - const renderTemplate = (node: StepperNode, template: SyntaxTemplatePart[]): React.ReactNode => { - const childContext = (extra: Partial): RenderContext => ({ - styleWrapper, - popoverDepth, - profile: renderContext.profile, - ...extra, - }); - const cls = (c?: string) => (c ? TOKEN_CLASS[c] : undefined); - const renderPart = (part: SyntaxTemplatePart, key: number): React.ReactNode => { - if (typeof part === 'string') return {part}; - if ('token' in part) - return ( - - {part.token} - - ); - if ('prop' in part) { - const value = readNodeProp(node, part.prop); - return ( - - {value == null ? '' : String(value)} - - ); - } - if ('child' in part) { - const child = node[part.child] as StepperNode | null | undefined; - return ( - - {renderNode(child, childContext({ parentNode: node, isRight: part.isRight }))} - - ); - } - if ('list' in part) { - const items = (node[part.list] as StepperNode[] | undefined) ?? []; - if (items.length === 0) return null; - return ( - - {part.prefix} - {items.map((item, i) => ( - - {i !== 0 ? part.sep : null} - {renderNode(item, childContext({}))} - - ))} - - ); - } - if ('block' in part) { - const items = (node[part.block] as StepperNode[] | undefined) ?? []; - return ( - - {items.map((item, i) => ( -
- {renderNode(item, childContext({}))} -
- ))} -
- ); - } - if ('lines' in part) { - const items = (node[part.lines] as StepperNode[] | undefined) ?? []; - return ( - - {items.map((item, i) => ( -
{renderNode(item, childContext({}))}
- ))} -
- ); - } - if ('when' in part) { - return node[part.when] ? ( - {part.parts.map((p, i) => renderPart(p, i))} - ) : null; - } - if ('unless' in part) { - return node[part.unless] ? null : ( - {part.parts.map((p, i) => renderPart(p, i))} - ); - } - if ('image' in part) { - const src = readNodeProp(node, part.image); - if (typeof src !== 'string' || !src.startsWith('data:')) return null; - const alt = part.altProp === undefined ? undefined : readNodeProp(node, part.altProp); - const altText = alt == null ? '' : String(alt); - return ( - } - > - {altText} - - ); - } - return null; - }; - return {template.map((part, i) => renderPart(part, i))}; - }; - - // Profile-driven function *values* (mu-term + popover), mirroring the built-in - // ArrowFunctionExpression behaviour for any language that declares its function-value node types - // (see SyntaxProfile.functionValues). A named function value collapses to its name with a hover - // popover showing its full definition; an anonymous one renders inline from its template. - const functionValueRuleFor = (type: string): FunctionValueRule | undefined => - renderContext.profile?.functionValues?.find(rule => rule.type === type); - - // Renders a named function value collapsed as its name, with a hover popover showing its body. The - // popover content is a component element (lazily rendered), never an eagerly-computed node, so a - // recursive function's nested popovers do not expand forever at render time. - const renderProfileFunctionValue = (funcNode: StepperNode, funcName: string): React.ReactNode => { - // Recursive hovering: identifiers in the body that refer back to this function (by name) are - // themselves wrapped in the same popover, so a recursive definition stays explorable. - const muTermWrapper: StyleWrapper = (targetNode: StepperNode) => - targetNode.type === 'Identifier' && targetNode.name === funcName - ? (preprocessed: React.ReactNode) => ( - - - } - > - {preprocessed} - - - ) - : (preprocessed: React.ReactNode) => preprocessed; - - return ( - - - } - > - {funcName} - - - ); - }; - - // Entry point of rendering. With a language profile, render the node generically from its template - // (collapsing a named function value to a mu-term); otherwise fall back to the built-in - // Source/JavaScript renderers above. - const profile = renderContext.profile; - let isParenthesis = expressionNeedsParenthesis( - currentNode, - renderContext.parentNode, - renderContext.isRight, - profile, - ); - let result: React.ReactNode; - if (profile) { - const functionRule = functionValueRuleFor(currentNode.type); - const funcName = functionRule ? readNodeProp(currentNode, functionRule.nameProp) : undefined; - if (functionRule && funcName != null && funcName !== '' && !renderContext.expandFunctionValue) { - // A named function value: collapse to a mu-term. It is atomic, so never parenthesised. - result = renderProfileFunctionValue(currentNode, String(funcName)); - isParenthesis = false; - } else { - // A profile is authoritative: never fall back to JS syntax for an unmapped node type. - const template = profile.templates[currentNode.type]; - result = template ? renderTemplate(currentNode, template) : `<${currentNode.type}>`; - } - - // A fixed-text hover popover (e.g. a builtin's ``) is independent of the - // function-value mu-term collapse above and applies regardless of which branch produced `result` — - // a node type can be listed in both `functionValues` and `hoverText` and get both behaviours, not - // just whichever branch happened to run first. See SyntaxProfile.hoverText. - const hoverRule = profile.hoverText?.find(rule => rule.type === currentNode.type); - const hoverText = hoverRule ? readNodeProp(currentNode, hoverRule.textProp) : undefined; - if (typeof hoverText === 'string' && hoverText !== '') { - const content = result; - result = ( - } - > - {content} - - ); - } - } else { - const renderer = ( - renderers as unknown as Record React.ReactNode> - )[currentNode.type]; - result = renderer ? renderer(currentNode) : `<${currentNode.type}>`; // For debugging in case some AST renderer has not been implemented yet - } - if (isParenthesis) { - result = ( - - {'('} - {result} - {')'} - - ); - } - // custom wrapper style - if (styleWrapper) { - result = styleWrapper(currentNode)(result); - } - return result; -} -/////////////////////////////////// Custom AST Renderer for Stepper ////////////////////////////////// - -/** - * A React component that handles rendering of a single step's AST + markers. - */ -function CustomASTRenderer( - props: SerializedStepperStep & { profile?: SyntaxProfile }, -): React.ReactNode { - const getDisplayedNode = useCallback((): React.ReactNode => { - function markerStyleWrapper(node: StepperNode) { - return (rendered: React.ReactNode) => { - if (props.markers === undefined) { - return rendered; - } - // highlight the entire function declaration body if it's a function declaration, - // else just highlight that line - let returnNode = {rendered}; - props.markers.forEach(marker => { - // Match by stable node id rather than object identity, which does not survive - // serialization across the runner/host channel. - if (marker.redexId !== undefined && marker.redexId === node.nodeId) { - const Wrapper = node.type === 'FunctionDeclaration' ? 'div' : 'span'; - returnNode = {returnNode}; - } - }); - return returnNode; - }; - } - return renderNode(props.ast, { - styleWrapper: markerStyleWrapper, - popoverDepth: 0, - profile: props.profile, - }); - }, [props]); - return
{getDisplayedNode()}
; -} - -/** - * expressionNeedsParenthesis - * checks whether there should be parentheses wrapped around the node or not - */ -function expressionNeedsParenthesis( - node: StepperNode, - parentNode?: StepperNode, - isRightHand?: boolean, - profile?: SyntaxProfile, -) { - if (parentNode === undefined) { - return false; - } - - // A profile supplies its language's precedence; otherwise use the built-in JavaScript tables. - const exprPrecedence = profile?.expressionPrecedence ?? EXPRESSIONS_PRECEDENCE; - const opPrecedence = profile?.operatorPrecedence ?? OPERATOR_PRECEDENCE; - - const nodePrecedence = exprPrecedence[node.type as keyof typeof exprPrecedence] as - | number - | undefined; - if (nodePrecedence === NEEDS_PARENTHESES) { - return true; - } - const parentNodePrecedence = exprPrecedence[parentNode.type as keyof typeof exprPrecedence] as - | number - | undefined; - if (nodePrecedence === undefined || parentNodePrecedence === undefined) { - return false; - } - - if (nodePrecedence !== parentNodePrecedence) { - return ( - (!isRightHand && nodePrecedence === 15 && parentNodePrecedence === 14) || - nodePrecedence < parentNodePrecedence - ); - } - - if (!('operator' in node) || !('operator' in parentNode)) { - return false; - } - - if (nodePrecedence !== 13 && nodePrecedence !== 14) { - // Not a `LogicalExpression` or `BinaryExpression` - return false; - } - if (node.operator === '**' && parentNode.operator === '**') { - // Exponentiation operator has right-to-left associativity - return !isRightHand; - } - if ( - nodePrecedence === 13 && - parentNodePrecedence === 13 && - (node.operator === '??' || parentNode.operator === '??') - ) { - return true; - } - - const nodeOperatorPrecedence = opPrecedence[node.operator as keyof typeof opPrecedence]; - const parentNodeOperatorPrecedence = - opPrecedence[parentNode.operator as keyof typeof opPrecedence]; - return isRightHand - ? nodeOperatorPrecedence <= parentNodeOperatorPrecedence - : nodeOperatorPrecedence <= parentNodeOperatorPrecedence; -} -const OPERATOR_PRECEDENCE = { - '||': 2, - '??': 3, - '&&': 4, - '|': 5, - '^': 6, - '&': 7, - '==': 8, - '!=': 8, - '===': 8, - '!==': 8, - '<': 9, - '>': 9, - '<=': 9, - '>=': 9, - in: 9, - instanceof: 9, - '<<': 10, - '>>': 10, - '>>>': 10, - '+': 11, - '-': 11, - '*': 12, - '%': 12, - '/': 12, - '**': 13, -}; -const NEEDS_PARENTHESES = 17; -const EXPRESSIONS_PRECEDENCE = { - // Definitions - ArrayExpression: 20, - TaggedTemplateExpression: 20, - ThisExpression: 20, - Identifier: 20, - PrivateIdentifier: 20, - Literal: 18, - TemplateLiteral: 20, - Super: 20, - SequenceExpression: 20, - // Operations - MemberExpression: 19, - ChainExpression: 19, - CallExpression: 19, - NewExpression: 19, - // Other definitions - ArrowFunctionExpression: NEEDS_PARENTHESES, - ClassExpression: NEEDS_PARENTHESES, - FunctionExpression: NEEDS_PARENTHESES, - ObjectExpression: NEEDS_PARENTHESES, - // Other operations - UpdateExpression: 16, - UnaryExpression: 15, - AwaitExpression: 15, - BinaryExpression: 14, - LogicalExpression: 13, - ConditionalExpression: 4, - AssignmentExpression: 3, - YieldExpression: 2, - RestElement: 1, -}; -======= ->>>>>>> origin/main diff --git a/src/web/stepper/src/__tests__/SubstVisualizer.test.tsx b/src/web/stepper/src/__tests__/SubstVisualizer.test.tsx index 55e6d8c..d60c0ec 100644 --- a/src/web/stepper/src/__tests__/SubstVisualizer.test.tsx +++ b/src/web/stepper/src/__tests__/SubstVisualizer.test.tsx @@ -8,16 +8,16 @@ import { act } from "react"; import TestRenderer from "react-test-renderer"; import { describe, expect, test, vi } from "vitest"; +import StepperView from "../SubstVisualizer"; +import { sliderLabels } from "../sliderLabels"; + // The hotkey library needs react-dom, which it does not declare (so it does not resolve here). // (Here it runs the binding for the key pressed, as the real one does.) -vi.mock("@mantine/hooks", () => ({ +vi.mock(import('@mantine/hooks'), () => ({ getHotkeyHandler: (bindings: [string, () => void][]) => (event: { key: string }) => bindings.find(([key]) => key === event.key)?.[1](), })); -import { sliderLabels } from "../sliderLabels"; -import StepperView from "../SubstVisualizer"; - const step = (explanation: string): SerializedStepperStep => ({ ast: { type: "Program", nodeId: "p", body: [] }, markers: [{ explanation }], @@ -162,7 +162,7 @@ describe("breakpoints and keys", () => { }); }); -describe("sliderLabels", () => { +describe(sliderLabels, () => { test("labels 0, a round step apart, and always the last, never crowding it", () => { expect(sliderLabels(0)).toEqual([0]); expect(sliderLabels(1)).toEqual([0, 1]); diff --git a/src/web/stepper/src/render.tsx b/src/web/stepper/src/render.tsx index adcbc69..2794134 100644 --- a/src/web/stepper/src/render.tsx +++ b/src/web/stepper/src/render.tsx @@ -14,7 +14,7 @@ import { useCallback } from "react"; * language-specific fields (e.g. `left`, `operator`, `params`) without per-node typing, exactly as * the original (class-based) renderer did after casting. */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -- language-specific AST fields are read untyped, exactly as the original class-based renderer did after casting + export type StepperNode = SerializedStepperNode & Record; /** diff --git a/yarn.lock b/yarn.lock index 5eb26e6..4c4f0ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,7 +5,7 @@ __metadata: version: 10 cacheKey: 10c0 -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.27.1, @babel/code-frame@npm:^7.29.7": +"@babel/code-frame@npm:^7.29.7": version: 7.29.7 resolution: "@babel/code-frame@npm:7.29.7" dependencies: @@ -40,7 +40,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.23.9, @babel/core@npm:^7.27.4, @babel/core@npm:^7.29.0": +"@babel/core@npm:^7.29.0": version: 7.29.7 resolution: "@babel/core@npm:7.29.7" dependencies: @@ -87,7 +87,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.27.5, @babel/generator@npm:^7.29.7": +"@babel/generator@npm:^7.29.7": version: 7.29.7 resolution: "@babel/generator@npm:7.29.7" dependencies: @@ -177,13 +177,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.29.7, @babel/helper-plugin-utils@npm:^7.8.0": - version: 7.29.7 - resolution: "@babel/helper-plugin-utils@npm:7.29.7" - checksum: 10c0/380477a06133274a2759f9355929cb60a95e8b8fee624a1ae1fa349e1d1645b89daca456f72833f6d1062bffa12ee4271c5bf0cc5a61c0166cdc24c7591e2408 - languageName: node - linkType: hard - "@babel/helper-string-parser@npm:^7.29.7": version: 7.29.7 resolution: "@babel/helper-string-parser@npm:7.29.7" @@ -246,7 +239,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.29.3, @babel/parser@npm:^7.29.7": +"@babel/parser@npm:^7.29.3, @babel/parser@npm:^7.29.7": version: 7.29.7 resolution: "@babel/parser@npm:7.29.7" dependencies: @@ -268,193 +261,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-async-generators@npm:^7.8.4": - version: 7.8.4 - resolution: "@babel/plugin-syntax-async-generators@npm:7.8.4" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/d13efb282838481348c71073b6be6245b35d4f2f964a8f71e4174f235009f929ef7613df25f8d2338e2d3e44bc4265a9f8638c6aaa136d7a61fe95985f9725c8 - languageName: node - linkType: hard - -"@babel/plugin-syntax-bigint@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-bigint@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/686891b81af2bc74c39013655da368a480f17dd237bf9fbc32048e5865cb706d5a8f65438030da535b332b1d6b22feba336da8fa931f663b6b34e13147d12dde - languageName: node - linkType: hard - -"@babel/plugin-syntax-class-properties@npm:^7.12.13": - version: 7.12.13 - resolution: "@babel/plugin-syntax-class-properties@npm:7.12.13" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.12.13" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/95168fa186416195280b1264fb18afcdcdcea780b3515537b766cb90de6ce042d42dd6a204a39002f794ae5845b02afb0fd4861a3308a861204a55e68310a120 - languageName: node - linkType: hard - -"@babel/plugin-syntax-class-static-block@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/plugin-syntax-class-static-block@npm:7.14.5" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.14.5" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/4464bf9115f4a2d02ce1454411baf9cfb665af1da53709c5c56953e5e2913745b0fcce82982a00463d6facbdd93445c691024e310b91431a1e2f024b158f6371 - languageName: node - linkType: hard - -"@babel/plugin-syntax-import-attributes@npm:^7.24.7": - version: 7.29.7 - resolution: "@babel/plugin-syntax-import-attributes@npm:7.29.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.29.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/b9a47e869d8c06676297069895ae34e2bd244ec4c3bdf15002f1e69aed32eef0361044af22a43f271b8de5e23a40534fe6a74a63e7ab98e3d60a74b322444b49 - languageName: node - linkType: hard - -"@babel/plugin-syntax-import-meta@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/plugin-syntax-import-meta@npm:7.10.4" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.10.4" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/0b08b5e4c3128523d8e346f8cfc86824f0da2697b1be12d71af50a31aff7a56ceb873ed28779121051475010c28d6146a6bfea8518b150b71eeb4e46190172ee - languageName: node - linkType: hard - -"@babel/plugin-syntax-json-strings@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-json-strings@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/e98f31b2ec406c57757d115aac81d0336e8434101c224edd9a5c93cefa53faf63eacc69f3138960c8b25401315af03df37f68d316c151c4b933136716ed6906e - languageName: node - linkType: hard - -"@babel/plugin-syntax-jsx@npm:^7.27.1": - version: 7.29.7 - resolution: "@babel/plugin-syntax-jsx@npm:7.29.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.29.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/1736000de183538ba8eef34520105508860e48b0c763254ba9158af5e814ed8bbceeedbb4281fbda33de787ae5b3870e92f60c6ae7131e7d322e451d57387896 - languageName: node - linkType: hard - -"@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.10.4" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/2594cfbe29411ad5bc2ad4058de7b2f6a8c5b86eda525a993959438615479e59c012c14aec979e538d60a584a1a799b60d1b8942c3b18468cb9d99b8fd34cd0b - languageName: node - linkType: hard - -"@babel/plugin-syntax-nullish-coalescing-operator@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-nullish-coalescing-operator@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/2024fbb1162899094cfc81152449b12bd0cc7053c6d4bda8ac2852545c87d0a851b1b72ed9560673cbf3ef6248257262c3c04aabf73117215c1b9cc7dd2542ce - languageName: node - linkType: hard - -"@babel/plugin-syntax-numeric-separator@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/plugin-syntax-numeric-separator@npm:7.10.4" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.10.4" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/c55a82b3113480942c6aa2fcbe976ff9caa74b7b1109ff4369641dfbc88d1da348aceb3c31b6ed311c84d1e7c479440b961906c735d0ab494f688bf2fd5b9bb9 - languageName: node - linkType: hard - -"@babel/plugin-syntax-object-rest-spread@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-object-rest-spread@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/ee1eab52ea6437e3101a0a7018b0da698545230015fc8ab129d292980ec6dff94d265e9e90070e8ae5fed42f08f1622c14c94552c77bcac784b37f503a82ff26 - languageName: node - linkType: hard - -"@babel/plugin-syntax-optional-catch-binding@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-optional-catch-binding@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/27e2493ab67a8ea6d693af1287f7e9acec206d1213ff107a928e85e173741e1d594196f99fec50e9dde404b09164f39dec5864c767212154ffe1caa6af0bc5af - languageName: node - linkType: hard - -"@babel/plugin-syntax-optional-chaining@npm:^7.8.3": - version: 7.8.3 - resolution: "@babel/plugin-syntax-optional-chaining@npm:7.8.3" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.8.0" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/46edddf2faa6ebf94147b8e8540dfc60a5ab718e2de4d01b2c0bdf250a4d642c2bd47cbcbb739febcb2bf75514dbcefad3c52208787994b8d0f8822490f55e81 - languageName: node - linkType: hard - -"@babel/plugin-syntax-private-property-in-object@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/plugin-syntax-private-property-in-object@npm:7.14.5" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.14.5" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/69822772561706c87f0a65bc92d0772cea74d6bc0911537904a676d5ff496a6d3ac4e05a166d8125fce4a16605bace141afc3611074e170a994e66e5397787f3 - languageName: node - linkType: hard - -"@babel/plugin-syntax-top-level-await@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/plugin-syntax-top-level-await@npm:7.14.5" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.14.5" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/14bf6e65d5bc1231ffa9def5f0ef30b19b51c218fcecaa78cd1bdf7939dfdf23f90336080b7f5196916368e399934ce5d581492d8292b46a2fb569d8b2da106f - languageName: node - linkType: hard - -"@babel/plugin-syntax-typescript@npm:^7.27.1": - version: 7.29.7 - resolution: "@babel/plugin-syntax-typescript@npm:7.29.7" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.29.7" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/c49883b0327e8683b770dc823205af5c697da216e590dcf5bf53f3f031e7e381de450b164f8f99853f0837a3de5cb793298e2be6697a0f6e452bb9dd34b5165e - languageName: node - linkType: hard - "@babel/runtime@npm:^7.26.10, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.7": version: 7.29.7 resolution: "@babel/runtime@npm:7.29.7" @@ -514,7 +320,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.29.0, @babel/types@npm:^7.29.7": +"@babel/types@npm:^7.29.0, @babel/types@npm:^7.29.7": version: 7.29.7 resolution: "@babel/types@npm:7.29.7" dependencies: @@ -534,13 +340,6 @@ __metadata: languageName: node linkType: hard -"@bcoe/v8-coverage@npm:^0.2.3": - version: 0.2.3 - resolution: "@bcoe/v8-coverage@npm:0.2.3" - checksum: 10c0/6b80ae4cb3db53f486da2dc63b6e190a74c8c3cca16bb2733f234a0b6a9382b09b146488ae08e2b22cf00f6c83e20f3e040a2f7894f05c045c946d6a090b1d52 - languageName: node - linkType: hard - "@blueprintjs/colors@npm:^5.1.16": version: 5.1.16 resolution: "@blueprintjs/colors@npm:5.1.16" @@ -992,20 +791,6 @@ __metadata: languageName: node linkType: hard -"@isaacs/cliui@npm:^8.0.2": - version: 8.0.2 - resolution: "@isaacs/cliui@npm:8.0.2" - dependencies: - string-width: "npm:^5.1.2" - string-width-cjs: "npm:string-width@^4.2.0" - strip-ansi: "npm:^7.0.1" - strip-ansi-cjs: "npm:strip-ansi@^6.0.1" - wrap-ansi: "npm:^8.1.0" - wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" - checksum: 10c0/b1bf42535d49f11dc137f18d5e4e63a28c5569de438a221c369483731e9dac9fb797af554e8bf02b6192d1e5eba6e6402cf93900c3d0ac86391d00d04876789e - languageName: node - linkType: hard - "@isaacs/fs-minipass@npm:^4.0.0": version: 4.0.1 resolution: "@isaacs/fs-minipass@npm:4.0.1" @@ -1015,291 +800,13 @@ __metadata: languageName: node linkType: hard -"@istanbuljs/load-nyc-config@npm:^1.0.0": - version: 1.1.0 - resolution: "@istanbuljs/load-nyc-config@npm:1.1.0" - dependencies: - camelcase: "npm:^5.3.1" - find-up: "npm:^4.1.0" - get-package-type: "npm:^0.1.0" - js-yaml: "npm:^3.13.1" - resolve-from: "npm:^5.0.0" - checksum: 10c0/dd2a8b094887da5a1a2339543a4933d06db2e63cbbc2e288eb6431bd832065df0c099d091b6a67436e71b7d6bf85f01ce7c15f9253b4cbebcc3b9a496165ba42 - languageName: node - linkType: hard - -"@istanbuljs/schema@npm:^0.1.2, @istanbuljs/schema@npm:^0.1.3": +"@istanbuljs/schema@npm:^0.1.3": version: 0.1.6 resolution: "@istanbuljs/schema@npm:0.1.6" checksum: 10c0/bb0d370bf3dd454d2f37f1bccb8921e2da99adacef2da56ef47850e25d7a4de69cf639ead8c189755aef38921369024b4afea3535a5c2ac9082b3e1171bcbc3a languageName: node linkType: hard -"@jest/console@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/console@npm:30.4.1" - dependencies: - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - chalk: "npm:^4.1.2" - jest-message-util: "npm:30.4.1" - jest-util: "npm:30.4.1" - slash: "npm:^3.0.0" - checksum: 10c0/f782722ef5754ab864b996000cf1f0545f7be9db6ba8f89cb2381dfab9910a52c59a830e5ea069a76840023e40806493d9900d8eb7e9821d23a11a498f32739e - languageName: node - linkType: hard - -"@jest/core@npm:30.4.2": - version: 30.4.2 - resolution: "@jest/core@npm:30.4.2" - dependencies: - "@jest/console": "npm:30.4.1" - "@jest/pattern": "npm:30.4.0" - "@jest/reporters": "npm:30.4.1" - "@jest/test-result": "npm:30.4.1" - "@jest/transform": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - ansi-escapes: "npm:^4.3.2" - chalk: "npm:^4.1.2" - ci-info: "npm:^4.2.0" - exit-x: "npm:^0.2.2" - fast-json-stable-stringify: "npm:^2.1.0" - graceful-fs: "npm:^4.2.11" - jest-changed-files: "npm:30.4.1" - jest-config: "npm:30.4.2" - jest-haste-map: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-regex-util: "npm:30.4.0" - jest-resolve: "npm:30.4.1" - jest-resolve-dependencies: "npm:30.4.2" - jest-runner: "npm:30.4.2" - jest-runtime: "npm:30.4.2" - jest-snapshot: "npm:30.4.1" - jest-util: "npm:30.4.1" - jest-validate: "npm:30.4.1" - jest-watcher: "npm:30.4.1" - pretty-format: "npm:30.4.1" - slash: "npm:^3.0.0" - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - checksum: 10c0/4237ec79d5403b82ba89e3be6e4318d9f37c3a11281bd76cfbdd4ff08d8c89850555607c4d494dab3526e01a90db3539e549017883967dd392b5084f1be0d5b2 - languageName: node - linkType: hard - -"@jest/diff-sequences@npm:30.4.0": - version: 30.4.0 - resolution: "@jest/diff-sequences@npm:30.4.0" - checksum: 10c0/b4358b1b885098b905cb777f58788ddd45f90c4ebc3ce2c04fb1d4c9516f35ac2d9daef8263cd21c537bd7a52ab320f03e4ba9521677959ae20e3d405356b420 - languageName: node - linkType: hard - -"@jest/environment@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/environment@npm:30.4.1" - dependencies: - "@jest/fake-timers": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - jest-mock: "npm:30.4.1" - checksum: 10c0/704987ff8650c91a8ed13796ce47e9c55da3c12a01902d9e384330cead18eb4d34ce665a8d9962dddf2736fac006f92efc1039b8da424adf8fdc16f8d81aff6c - languageName: node - linkType: hard - -"@jest/expect-utils@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/expect-utils@npm:30.4.1" - dependencies: - "@jest/get-type": "npm:30.1.0" - checksum: 10c0/6dea9e11ebcc7be68fea5950ae5a1b7ff9fd1490101ee8af0aede336b9934ab24a28bcafe2f1171dac0f95982406386c609ca2659b9132e1a9d419e8d69b9cd4 - languageName: node - linkType: hard - -"@jest/expect@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/expect@npm:30.4.1" - dependencies: - expect: "npm:30.4.1" - jest-snapshot: "npm:30.4.1" - checksum: 10c0/2133183e735982879408036237b115abc2e57fa52bb7324be0a1f2ab6941a57da93b2e6f498dc110b7d007dd20463013fbcc5b24377cf65e6a8518d3b2ff76bd - languageName: node - linkType: hard - -"@jest/fake-timers@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/fake-timers@npm:30.4.1" - dependencies: - "@jest/types": "npm:30.4.1" - "@sinonjs/fake-timers": "npm:^15.4.0" - "@types/node": "npm:*" - jest-message-util: "npm:30.4.1" - jest-mock: "npm:30.4.1" - jest-util: "npm:30.4.1" - checksum: 10c0/4a10e4eb64bb5ea2531cdcc79f3058731f5c14faf2a74f498fcb37f6690c3c0f9b12a9856736d26e34631eb38db12e12812da71de27b9d332df44dda9f460fbe - languageName: node - linkType: hard - -"@jest/get-type@npm:30.1.0": - version: 30.1.0 - resolution: "@jest/get-type@npm:30.1.0" - checksum: 10c0/3e65fd5015f551c51ec68fca31bbd25b466be0e8ee8075d9610fa1c686ea1e70a942a0effc7b10f4ea9a338c24337e1ad97ff69d3ebacc4681b7e3e80d1b24ac - languageName: node - linkType: hard - -"@jest/globals@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/globals@npm:30.4.1" - dependencies: - "@jest/environment": "npm:30.4.1" - "@jest/expect": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - jest-mock: "npm:30.4.1" - checksum: 10c0/7961eefdc9e69ba7754d11a1bae4bc2960f33e03d9c1d6c73f27895b8cf92a9118a234330f31dc8efe16e835fe70ef9cc6c26f60121f6b6e9fac71c8b1bcd709 - languageName: node - linkType: hard - -"@jest/pattern@npm:30.4.0": - version: 30.4.0 - resolution: "@jest/pattern@npm:30.4.0" - dependencies: - "@types/node": "npm:*" - jest-regex-util: "npm:30.4.0" - checksum: 10c0/05bc0799f84f3750bbbff0f9a546979efd0dbcee86c1be98b9e2811a68885809ec7b5cca39b8dda1497cb7cf17b7be936019fba8dfbcd9c53b181e03e67f4f82 - languageName: node - linkType: hard - -"@jest/reporters@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/reporters@npm:30.4.1" - dependencies: - "@bcoe/v8-coverage": "npm:^0.2.3" - "@jest/console": "npm:30.4.1" - "@jest/test-result": "npm:30.4.1" - "@jest/transform": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@jridgewell/trace-mapping": "npm:^0.3.25" - "@types/node": "npm:*" - chalk: "npm:^4.1.2" - collect-v8-coverage: "npm:^1.0.2" - exit-x: "npm:^0.2.2" - glob: "npm:^10.5.0" - graceful-fs: "npm:^4.2.11" - istanbul-lib-coverage: "npm:^3.0.0" - istanbul-lib-instrument: "npm:^6.0.0" - istanbul-lib-report: "npm:^3.0.0" - istanbul-lib-source-maps: "npm:^5.0.0" - istanbul-reports: "npm:^3.1.3" - jest-message-util: "npm:30.4.1" - jest-util: "npm:30.4.1" - jest-worker: "npm:30.4.1" - slash: "npm:^3.0.0" - string-length: "npm:^4.0.2" - v8-to-istanbul: "npm:^9.0.1" - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - checksum: 10c0/cf5220462c6242fa564bbeb6d5988ebfd814e0351f3bddae07323b55c68c7ebd4aa4c23e717231ab4b2d63c4fc7fa4615b9dad8584be534bd44622981242dceb - languageName: node - linkType: hard - -"@jest/schemas@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/schemas@npm:30.4.1" - dependencies: - "@sinclair/typebox": "npm:^0.34.0" - checksum: 10c0/96f388ebfc1974457fcbde2ad36c40a0b549cba3f624fe8d9d6e5903a152dc75e4043f4ac9ac7668622f2ecb0f9a4dcb9a38edf3bc0d52b82045b2bb2b69b72a - languageName: node - linkType: hard - -"@jest/snapshot-utils@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/snapshot-utils@npm:30.4.1" - dependencies: - "@jest/types": "npm:30.4.1" - chalk: "npm:^4.1.2" - graceful-fs: "npm:^4.2.11" - natural-compare: "npm:^1.4.0" - checksum: 10c0/81da9079719eece02b89c45cb97162b5b7d794981652c8d8fe2846843ac81ce219ea4bc21bde7cf76c9032006435f82bd9aee8d6139d90b77078ddad4865af02 - languageName: node - linkType: hard - -"@jest/source-map@npm:30.0.1": - version: 30.0.1 - resolution: "@jest/source-map@npm:30.0.1" - dependencies: - "@jridgewell/trace-mapping": "npm:^0.3.25" - callsites: "npm:^3.1.0" - graceful-fs: "npm:^4.2.11" - checksum: 10c0/e7bda2786fc9f483d9dd7566c58c4bd948830997be862dfe80a3ae5550ff3f84753abb52e705d02ebe9db9f34ba7ebec4c2db11882048cdeef7a66f6332b3897 - languageName: node - linkType: hard - -"@jest/test-result@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/test-result@npm:30.4.1" - dependencies: - "@jest/console": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/istanbul-lib-coverage": "npm:^2.0.6" - collect-v8-coverage: "npm:^1.0.2" - checksum: 10c0/920fa3fe3cc8b5e11bfe36066d733030f1245865d7cac4862e3783a96f9c0a087fd8073c8cb56e4c87c6fcc97b46e6f828ecd3b10dd8e208f5e1b983fcc5cdb8 - languageName: node - linkType: hard - -"@jest/test-sequencer@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/test-sequencer@npm:30.4.1" - dependencies: - "@jest/test-result": "npm:30.4.1" - graceful-fs: "npm:^4.2.11" - jest-haste-map: "npm:30.4.1" - slash: "npm:^3.0.0" - checksum: 10c0/531b19ffb2358b3b22a56b306359acf66db2073978dd6df8a9522b5b4034ad7540a9cb84bdfebbcb2872686d6d2ab8cabea04ad23ef9d4488cbafd03f7511501 - languageName: node - linkType: hard - -"@jest/transform@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/transform@npm:30.4.1" - dependencies: - "@babel/core": "npm:^7.27.4" - "@jest/types": "npm:30.4.1" - "@jridgewell/trace-mapping": "npm:^0.3.25" - babel-plugin-istanbul: "npm:^7.0.1" - chalk: "npm:^4.1.2" - convert-source-map: "npm:^2.0.0" - fast-json-stable-stringify: "npm:^2.1.0" - graceful-fs: "npm:^4.2.11" - jest-haste-map: "npm:30.4.1" - jest-regex-util: "npm:30.4.0" - jest-util: "npm:30.4.1" - pirates: "npm:^4.0.7" - slash: "npm:^3.0.0" - write-file-atomic: "npm:^5.0.1" - checksum: 10c0/194f463f179f6ab3ccd6f4f0f03a117e3c01a7ce098ebf562250aca4c900ed3a9ec08b694227788eabd7cb4e0597f1d0788077c7550ddc679f68a0ad21cc87e0 - languageName: node - linkType: hard - -"@jest/types@npm:30.4.1": - version: 30.4.1 - resolution: "@jest/types@npm:30.4.1" - dependencies: - "@jest/pattern": "npm:30.4.0" - "@jest/schemas": "npm:30.4.1" - "@types/istanbul-lib-coverage": "npm:^2.0.6" - "@types/istanbul-reports": "npm:^3.0.4" - "@types/node": "npm:*" - "@types/yargs": "npm:^17.0.33" - chalk: "npm:^4.1.2" - checksum: 10c0/4c79f6dbdb1c7eaab5da255fc696c7cae744759d4020e42da8aa63b37fe55ce594be73075fe1ee5407dd59d7e47975be9f674bfc81e91bae2c89c62d27ba55a1 - languageName: node - linkType: hard - "@jridgewell/gen-mapping@npm:0.4.0-beta.0": version: 0.4.0-beta.0 resolution: "@jridgewell/gen-mapping@npm:0.4.0-beta.0" @@ -1361,7 +868,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:0.3.31, @jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.28, @jridgewell/trace-mapping@npm:^0.3.31": +"@jridgewell/trace-mapping@npm:0.3.31, @jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.28, @jridgewell/trace-mapping@npm:^0.3.31": version: 0.3.31 resolution: "@jridgewell/trace-mapping@npm:0.3.31" dependencies: @@ -1429,20 +936,6 @@ __metadata: languageName: node linkType: hard -"@pkgjs/parseargs@npm:^0.11.0": - version: 0.11.0 - resolution: "@pkgjs/parseargs@npm:0.11.0" - checksum: 10c0/5bd7576bb1b38a47a7fc7b51ac9f38748e772beebc56200450c4a817d712232b8f1d3ef70532c80840243c657d491cf6a6be1e3a214cff907645819fdc34aadd - languageName: node - linkType: hard - -"@pkgr/core@npm:^0.3.6": - version: 0.3.6 - resolution: "@pkgr/core@npm:0.3.6" - checksum: 10c0/153f0f4563f505faeba13c733efa0e05e467ce1c6b941055a5fd3b4560da60fbf1dff4b11da0075f034ddda11f2842b90395f60895dde5825875b616edccc11c - languageName: node - linkType: hard - "@pnpm/deps.graph-sequencer@npm:^1100.0.1": version: 1100.0.1 resolution: "@pnpm/deps.graph-sequencer@npm:1100.0.1" @@ -1569,7 +1062,7 @@ __metadata: languageName: node linkType: hard -"@rollup/plugin-commonjs@npm:^29.0.0, @rollup/plugin-commonjs@npm:^29.0.3": +"@rollup/plugin-commonjs@npm:^29.0.3": version: 29.0.3 resolution: "@rollup/plugin-commonjs@npm:29.0.3" dependencies: @@ -1848,31 +1341,6 @@ __metadata: languageName: node linkType: hard -"@sinclair/typebox@npm:^0.34.0": - version: 0.34.49 - resolution: "@sinclair/typebox@npm:0.34.49" - checksum: 10c0/16b7d87f039a49b68c10bb4cdcae2ce5242b2472228851fd6483731616aba4ef977690aa517b230a8d20da8185bb416eb34e326f30568b3963c1cf26b05d1ad8 - languageName: node - linkType: hard - -"@sinonjs/commons@npm:^3.0.1": - version: 3.0.1 - resolution: "@sinonjs/commons@npm:3.0.1" - dependencies: - type-detect: "npm:4.0.8" - checksum: 10c0/1227a7b5bd6c6f9584274db996d7f8cee2c8c350534b9d0141fc662eaf1f292ea0ae3ed19e5e5271c8fd390d27e492ca2803acd31a1978be2cdc6be0da711403 - languageName: node - linkType: hard - -"@sinonjs/fake-timers@npm:^15.4.0": - version: 15.4.0 - resolution: "@sinonjs/fake-timers@npm:15.4.0" - dependencies: - "@sinonjs/commons": "npm:^3.0.1" - checksum: 10c0/de4522afe0699fa8d3ae9d1715cbaa4b47e518c707bb7988a9ec6c7c67557d9f6df451f6be0338598b984a86f65aab9fab38dd9ce75a3c0ffb801a9500d5b10d - languageName: node - linkType: hard - "@sourceacademy/common-autocomplete@npm:^0.0.1, @sourceacademy/common-autocomplete@workspace:src/common/autocomplete": version: 0.0.0-use.local resolution: "@sourceacademy/common-autocomplete@workspace:src/common/autocomplete" @@ -1920,17 +1388,15 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/common-e-stepper@workspace:src/common/e-stepper" dependencies: - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-cse-machine": "workspace:*" "@sourceacademy/common-stepper": "workspace:*" - "@types/jest": "npm:^30.0.0" - jest: "npm:^30.4.2" - rollup: "npm:^4.60.2" - ts-jest: "npm:^29.4.11" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" + vitest: "catalog:" peerDependencies: "@sourceacademy/common-cse-machine": ">=0.3.1" "@sourceacademy/common-stepper": ">=0.0.4" @@ -2101,19 +1567,17 @@ __metadata: version: 0.0.0-use.local resolution: "@sourceacademy/runner-e-stepper@workspace:src/runner/e-stepper" dependencies: - "@rollup/plugin-node-resolve": "npm:^16.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-node-resolve": "catalog:" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-e-stepper": "workspace:*" "@sourceacademy/common-stepper": "workspace:*" - "@sourceacademy/conductor": "npm:^0.8.0" + "@sourceacademy/conductor": "catalog:" "@sourceacademy/runner-stepper": "workspace:*" - "@types/jest": "npm:^30.0.0" - jest: "npm:^30.4.2" - rollup: "npm:^4.60.2" - ts-jest: "npm:^29.4.11" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" + vitest: "catalog:" peerDependencies: "@sourceacademy/common-e-stepper": ">=0.0.0" "@sourceacademy/common-stepper": ">=0.0.4" @@ -2287,16 +1751,16 @@ __metadata: dependencies: "@blueprintjs/core": "npm:^6.0.0" "@blueprintjs/icons": "npm:^6.0.0" - "@rollup/plugin-commonjs": "npm:^29.0.0" - "@rollup/plugin-node-resolve": "npm:^16.0.3" + "@rollup/plugin-commonjs": "catalog:" + "@rollup/plugin-node-resolve": "catalog:" "@rollup/plugin-replace": "npm:^6.0.3" - "@rollup/plugin-terser": "npm:^1.0.0" - "@rollup/plugin-typescript": "npm:^12.3.0" + "@rollup/plugin-terser": "catalog:" + "@rollup/plugin-typescript": "catalog:" "@sourceacademy/common-cse-machine": "workspace:*" "@sourceacademy/common-e-stepper": "workspace:*" "@sourceacademy/common-stepper": "workspace:*" "@sourceacademy/common-tabs": "workspace:^" - "@sourceacademy/conductor": "npm:^0.8.0" + "@sourceacademy/conductor": "catalog:" "@types/react": "npm:^19.0.0" "@types/react-test-renderer": "npm:^19.0.0" classnames: "npm:^2.3.2" @@ -2305,9 +1769,10 @@ __metadata: react-dom: "npm:^19.0.0" react-konva: "npm:^19.0.0" react-test-renderer: "npm:^19.0.0" - rollup: "npm:^4.60.2" - tslib: "npm:^2.8.1" - typescript: "npm:^6.0.3" + rollup: "catalog:" + tslib: "catalog:" + typescript: "catalog:" + vitest: "catalog:" peerDependencies: "@blueprintjs/core": ">=5" konva: ">=9" @@ -2399,47 +1864,6 @@ __metadata: languageName: node linkType: hard -"@types/babel__core@npm:^7.20.5": - version: 7.20.5 - resolution: "@types/babel__core@npm:7.20.5" - dependencies: - "@babel/parser": "npm:^7.20.7" - "@babel/types": "npm:^7.20.7" - "@types/babel__generator": "npm:*" - "@types/babel__template": "npm:*" - "@types/babel__traverse": "npm:*" - checksum: 10c0/bdee3bb69951e833a4b811b8ee9356b69a61ed5b7a23e1a081ec9249769117fa83aaaf023bb06562a038eb5845155ff663e2d5c75dd95c1d5ccc91db012868ff - languageName: node - linkType: hard - -"@types/babel__generator@npm:*": - version: 7.27.0 - resolution: "@types/babel__generator@npm:7.27.0" - dependencies: - "@babel/types": "npm:^7.0.0" - checksum: 10c0/9f9e959a8792df208a9d048092fda7e1858bddc95c6314857a8211a99e20e6830bdeb572e3587ae8be5429e37f2a96fcf222a9f53ad232f5537764c9e13a2bbd - languageName: node - linkType: hard - -"@types/babel__template@npm:*": - version: 7.4.4 - resolution: "@types/babel__template@npm:7.4.4" - dependencies: - "@babel/parser": "npm:^7.1.0" - "@babel/types": "npm:^7.0.0" - checksum: 10c0/cc84f6c6ab1eab1427e90dd2b76ccee65ce940b778a9a67be2c8c39e1994e6f5bbc8efa309f6cea8dc6754994524cd4d2896558df76d92e7a1f46ecffee7112b - languageName: node - linkType: hard - -"@types/babel__traverse@npm:*": - version: 7.28.0 - resolution: "@types/babel__traverse@npm:7.28.0" - dependencies: - "@babel/types": "npm:^7.28.2" - checksum: 10c0/b52d7d4e8fc6a9018fe7361c4062c1c190f5778cf2466817cb9ed19d69fbbb54f9a85ffedeb748ed8062d2cf7d4cc088ee739848f47c57740de1c48cbf0d0994 - languageName: node - linkType: hard - "@types/chai@npm:^5.2.2": version: 5.2.3 resolution: "@types/chai@npm:5.2.3" @@ -2485,70 +1909,26 @@ __metadata: languageName: node linkType: hard -"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.1, @types/istanbul-lib-coverage@npm:^2.0.6": - version: 2.0.6 - resolution: "@types/istanbul-lib-coverage@npm:2.0.6" - checksum: 10c0/3948088654f3eeb45363f1db158354fb013b362dba2a5c2c18c559484d5eb9f6fd85b23d66c0a7c2fcfab7308d0a585b14dadaca6cc8bf89ebfdc7f8f5102fb7 +"@types/jsesc@npm:^2.5.0": + version: 2.5.1 + resolution: "@types/jsesc@npm:2.5.1" + checksum: 10c0/12ba7bf5968aeeb36408269f4b5a39718efc6411fa197cf0f5e967ba36ad7b7d555b78787fc480db43ce63ebe6ab0ffe5fd9f64b1ea3b0d073877f0747491b30 languageName: node linkType: hard -"@types/istanbul-lib-report@npm:*": - version: 3.0.3 - resolution: "@types/istanbul-lib-report@npm:3.0.3" - dependencies: - "@types/istanbul-lib-coverage": "npm:*" - checksum: 10c0/247e477bbc1a77248f3c6de5dadaae85ff86ac2d76c5fc6ab1776f54512a745ff2a5f791d22b942e3990ddbd40f3ef5289317c4fca5741bedfaa4f01df89051c +"@types/json-schema@npm:^7.0.15": + version: 7.0.15 + resolution: "@types/json-schema@npm:7.0.15" + checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db languageName: node linkType: hard -"@types/istanbul-reports@npm:^3.0.4": - version: 3.0.4 - resolution: "@types/istanbul-reports@npm:3.0.4" +"@types/node@npm:^25.9.1": + version: 25.9.1 + resolution: "@types/node@npm:25.9.1" dependencies: - "@types/istanbul-lib-report": "npm:*" - checksum: 10c0/1647fd402aced5b6edac87274af14ebd6b3a85447ef9ad11853a70fd92a98d35f81a5d3ea9fcb5dbb5834e800c6e35b64475e33fcae6bfa9acc70d61497c54ee - languageName: node - linkType: hard - -"@types/jest@npm:^30.0.0": - version: 30.0.0 - resolution: "@types/jest@npm:30.0.0" - dependencies: - expect: "npm:^30.0.0" - pretty-format: "npm:^30.0.0" - checksum: 10c0/20c6ce574154bc16f8dd6a97afacca4b8c4921a819496a3970382031c509ebe87a1b37b152a1b8475089b82d8ca951a9e95beb4b9bf78fbf579b1536f0b65969 - languageName: node - linkType: hard - -"@types/jsesc@npm:^2.5.0": - version: 2.5.1 - resolution: "@types/jsesc@npm:2.5.1" - checksum: 10c0/12ba7bf5968aeeb36408269f4b5a39718efc6411fa197cf0f5e967ba36ad7b7d555b78787fc480db43ce63ebe6ab0ffe5fd9f64b1ea3b0d073877f0747491b30 - languageName: node - linkType: hard - -"@types/json-schema@npm:^7.0.15": - version: 7.0.15 - resolution: "@types/json-schema@npm:7.0.15" - checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db - languageName: node - linkType: hard - -"@types/node@npm:*": - version: 26.6.4 - resolution: "@types/node@npm:26.6.4" - dependencies: - undici-types: "npm:~8.9.0" - checksum: 10c0/094e7362df9200ba4c2da09131ca3b5d1aa51535a268a24e4ee2546d9c5a323d1d30cbdaaaa3db7ef8b2387572d1f9edabb11538ab7a09d3bca33ae5cddc2b1b - languageName: node - linkType: hard - -"@types/node@npm:^25.9.1": - version: 25.9.1 - resolution: "@types/node@npm:25.9.1" - dependencies: - undici-types: "npm:>=7.24.0 <7.24.7" - checksum: 10c0/9a04682842bebbcf21a1779dfeab9aa733d7bd7bbc0a0edb641ab3a9a3d43eac543225acf669c334f458f1956443ebc072bc3c72840c543b8b356cab5c82d456 + undici-types: "npm:>=7.24.0 <7.24.7" + checksum: 10c0/9a04682842bebbcf21a1779dfeab9aa733d7bd7bbc0a0edb641ab3a9a3d43eac543225acf669c334f458f1956443ebc072bc3c72840c543b8b356cab5c82d456 languageName: node linkType: hard @@ -2604,29 +1984,6 @@ __metadata: languageName: node linkType: hard -"@types/stack-utils@npm:^2.0.3": - version: 2.0.3 - resolution: "@types/stack-utils@npm:2.0.3" - checksum: 10c0/1f4658385ae936330581bcb8aa3a066df03867d90281cdf89cc356d404bd6579be0f11902304e1f775d92df22c6dd761d4451c804b0a4fba973e06211e9bd77c - languageName: node - linkType: hard - -"@types/yargs-parser@npm:*": - version: 21.0.3 - resolution: "@types/yargs-parser@npm:21.0.3" - checksum: 10c0/e71c3bd9d0b73ca82e10bee2064c384ab70f61034bbfb78e74f5206283fc16a6d85267b606b5c22cb2a3338373586786fed595b2009825d6a9115afba36560a0 - languageName: node - linkType: hard - -"@types/yargs@npm:^17.0.33": - version: 17.0.35 - resolution: "@types/yargs@npm:17.0.35" - dependencies: - "@types/yargs-parser": "npm:*" - checksum: 10c0/609557826a6b85e73ccf587923f6429850d6dc70e420b455bab4601b670bfadf684b09ae288bccedab042c48ba65f1666133cf375814204b544009f57d6eef63 - languageName: node - linkType: hard - "@typescript-eslint/eslint-plugin@npm:8.65.0": version: 8.65.0 resolution: "@typescript-eslint/eslint-plugin@npm:8.65.0" @@ -2845,13 +2202,6 @@ __metadata: languageName: node linkType: hard -"@ungap/structured-clone@npm:^1.3.0": - version: 1.3.2 - resolution: "@ungap/structured-clone@npm:1.3.2" - checksum: 10c0/4d76bb376ec3e15f38bdffe045377807c79057daf54ae17eeb977c5b95efddd2d726b38c15aeb5d5c1a45c64ad03aa7e8b1a6dc67895480cba536ffd1c7a06ec - languageName: node - linkType: hard - "@unrs/resolver-binding-android-arm-eabi@npm:1.12.2": version: 1.12.2 resolution: "@unrs/resolver-binding-android-arm-eabi@npm:1.12.2" @@ -3184,71 +2534,6 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^4.3.2": - version: 4.3.2 - resolution: "ansi-escapes@npm:4.3.2" - dependencies: - type-fest: "npm:^0.21.3" - checksum: 10c0/da917be01871525a3dfcf925ae2977bc59e8c513d4423368645634bf5d4ceba5401574eb705c1e92b79f7292af5a656f78c5725a4b0e1cec97c4b413705c1d50 - languageName: node - linkType: hard - -"ansi-regex@npm:^5.0.1": - version: 5.0.1 - resolution: "ansi-regex@npm:5.0.1" - checksum: 10c0/9a64bb8627b434ba9327b60c027742e5d17ac69277960d041898596271d992d4d52ba7267a63ca10232e29f6107fc8a835f6ce8d719b88c5f8493f8254813737 - languageName: node - linkType: hard - -"ansi-regex@npm:^6.2.2": - version: 6.2.2 - resolution: "ansi-regex@npm:6.2.2" - checksum: 10c0/05d4acb1d2f59ab2cf4b794339c7b168890d44dda4bf0ce01152a8da0213aca207802f930442ce8cd22d7a92f44907664aac6508904e75e038fa944d2601b30f - languageName: node - linkType: hard - -"ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0": - version: 4.3.0 - resolution: "ansi-styles@npm:4.3.0" - dependencies: - color-convert: "npm:^2.0.1" - checksum: 10c0/895a23929da416f2bd3de7e9cb4eabd340949328ab85ddd6e484a637d8f6820d485f53933446f5291c3b760cbc488beb8e88573dd0f9c7daf83dccc8fe81b041 - languageName: node - linkType: hard - -"ansi-styles@npm:^5.2.0": - version: 5.2.0 - resolution: "ansi-styles@npm:5.2.0" - checksum: 10c0/9c4ca80eb3c2fb7b33841c210d2f20807f40865d27008d7c3f707b7f95cab7d67462a565e2388ac3285b71cb3d9bb2173de8da37c57692a362885ec34d6e27df - languageName: node - linkType: hard - -"ansi-styles@npm:^6.1.0": - version: 6.2.3 - resolution: "ansi-styles@npm:6.2.3" - checksum: 10c0/23b8a4ce14e18fb854693b95351e286b771d23d8844057ed2e7d083cd3e708376c3323707ec6a24365f7d7eda3ca00327fe04092e29e551499ec4c8b7bfac868 - languageName: node - linkType: hard - -"anymatch@npm:^3.1.3": - version: 3.1.3 - resolution: "anymatch@npm:3.1.3" - dependencies: - normalize-path: "npm:^3.0.0" - picomatch: "npm:^2.0.4" - checksum: 10c0/57b06ae984bc32a0d22592c87384cd88fe4511b1dd7581497831c56d41939c8a001b28e7b853e1450f2bf61992dfcaa8ae2d0d161a0a90c4fb631ef07098fbac - languageName: node - linkType: hard - -"argparse@npm:^1.0.7": - version: 1.0.10 - resolution: "argparse@npm:1.0.10" - dependencies: - sprintf-js: "npm:~1.0.2" - checksum: 10c0/b2972c5c23c63df66bca144dbc65d180efa74f25f8fd9b7d9a0a6c88ae839db32df3d54770dcb6460cf840d232b60695d1a6b1053f599d84e73f7437087712de - languageName: node - linkType: hard - "assertion-error@npm:^2.0.1": version: 2.0.1 resolution: "assertion-error@npm:2.0.1" @@ -3256,89 +2541,6 @@ __metadata: languageName: node linkType: hard -"babel-jest@npm:30.4.1": - version: 30.4.1 - resolution: "babel-jest@npm:30.4.1" - dependencies: - "@jest/transform": "npm:30.4.1" - "@types/babel__core": "npm:^7.20.5" - babel-plugin-istanbul: "npm:^7.0.1" - babel-preset-jest: "npm:30.4.0" - chalk: "npm:^4.1.2" - graceful-fs: "npm:^4.2.11" - slash: "npm:^3.0.0" - peerDependencies: - "@babel/core": ^7.11.0 || ^8.0.0-0 - checksum: 10c0/339b449011f31dc9eb18d9c49f0bb84e8de284e1107e64159a2f4a432bbd532d6a729774a56b7fbe76f5ddd716a0b4b7ad737265feab23b4d0225489b79a6f72 - languageName: node - linkType: hard - -"babel-plugin-istanbul@npm:^7.0.1": - version: 7.0.1 - resolution: "babel-plugin-istanbul@npm:7.0.1" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.0.0" - "@istanbuljs/load-nyc-config": "npm:^1.0.0" - "@istanbuljs/schema": "npm:^0.1.3" - istanbul-lib-instrument: "npm:^6.0.2" - test-exclude: "npm:^6.0.0" - checksum: 10c0/92975e3df12503b168695463b451468da0c20e117807221652eb8e33a26c160f3b9d4c5c4e65495657420e871c6a54e5e31f539e2e1da37ef2261d7ddd4b1dfd - languageName: node - linkType: hard - -"babel-plugin-jest-hoist@npm:30.4.0": - version: 30.4.0 - resolution: "babel-plugin-jest-hoist@npm:30.4.0" - dependencies: - "@types/babel__core": "npm:^7.20.5" - checksum: 10c0/1738ed536bb5ff536b4d406b8db7dbbd76cf10f80bb20d902e6efdda79898f045b9a991124d7104d8c398d0bd995d511d57694952645fba0f6250595a45277b0 - languageName: node - linkType: hard - -"babel-preset-current-node-syntax@npm:^1.2.0": - version: 1.2.0 - resolution: "babel-preset-current-node-syntax@npm:1.2.0" - dependencies: - "@babel/plugin-syntax-async-generators": "npm:^7.8.4" - "@babel/plugin-syntax-bigint": "npm:^7.8.3" - "@babel/plugin-syntax-class-properties": "npm:^7.12.13" - "@babel/plugin-syntax-class-static-block": "npm:^7.14.5" - "@babel/plugin-syntax-import-attributes": "npm:^7.24.7" - "@babel/plugin-syntax-import-meta": "npm:^7.10.4" - "@babel/plugin-syntax-json-strings": "npm:^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" - "@babel/plugin-syntax-numeric-separator": "npm:^7.10.4" - "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.3" - "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3" - "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" - "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5" - "@babel/plugin-syntax-top-level-await": "npm:^7.14.5" - peerDependencies: - "@babel/core": ^7.0.0 || ^8.0.0-0 - checksum: 10c0/94a4f81cddf9b051045d08489e4fff7336292016301664c138cfa3d9ffe3fe2ba10a24ad6ae589fd95af1ac72ba0216e1653555c187e694d7b17be0c002bea10 - languageName: node - linkType: hard - -"babel-preset-jest@npm:30.4.0": - version: 30.4.0 - resolution: "babel-preset-jest@npm:30.4.0" - dependencies: - babel-plugin-jest-hoist: "npm:30.4.0" - babel-preset-current-node-syntax: "npm:^1.2.0" - peerDependencies: - "@babel/core": ^7.11.0 || ^8.0.0-beta.1 - checksum: 10c0/ca2623aa4d8bf82b1fd01e5724a87cea7f80ff089341cf12415e9ce4b10f74838ecc6c8a48921f421f90bcd44f7929c0ad300146082e2f400253adb97ab5eb3a - languageName: node - linkType: hard - -"balanced-match@npm:^1.0.0": - version: 1.0.2 - resolution: "balanced-match@npm:1.0.2" - checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee - languageName: node - linkType: hard - "balanced-match@npm:^4.0.2": version: 4.0.4 resolution: "balanced-match@npm:4.0.4" @@ -3355,25 +2557,6 @@ __metadata: languageName: node linkType: hard -"brace-expansion@npm:^1.1.7": - version: 1.1.21 - resolution: "brace-expansion@npm:1.1.21" - dependencies: - balanced-match: "npm:^1.0.0" - concat-map: "npm:0.0.1" - checksum: 10c0/8f0a68a720f9cb28c2ecaea5cdaf15fafd686d6b6e4e722a81c9d4bd2f68c1fd0619731cac05875d842221eaf4d97d564d329a12eb3c4c22137502fb3c65be8b - languageName: node - linkType: hard - -"brace-expansion@npm:^2.0.2": - version: 2.1.1 - resolution: "brace-expansion@npm:2.1.1" - dependencies: - balanced-match: "npm:^1.0.0" - checksum: 10c0/63b5ddce608b70b50a76817c0526faf8ea67a9180073d88bb402f6bbc22a22da6b1dfac4f65efc53e5faa80222fb7d44bbf2fc638c3f55365975573f671d0ccb - languageName: node - linkType: hard - "brace-expansion@npm:^5.0.5": version: 5.0.5 resolution: "brace-expansion@npm:5.0.5" @@ -3407,24 +2590,6 @@ __metadata: languageName: node linkType: hard -"bs-logger@npm:^0.2.6": - version: 0.2.6 - resolution: "bs-logger@npm:0.2.6" - dependencies: - fast-json-stable-stringify: "npm:2.x" - checksum: 10c0/80e89aaaed4b68e3374ce936f2eb097456a0dddbf11f75238dbd53140b1e39259f0d248a5089ed456f1158984f22191c3658d54a713982f676709fbe1a6fa5a0 - languageName: node - linkType: hard - -"bser@npm:2.1.1": - version: 2.1.1 - resolution: "bser@npm:2.1.1" - dependencies: - node-int64: "npm:^0.4.0" - checksum: 10c0/24d8dfb7b6d457d73f32744e678a60cc553e4ec0e9e1a01cf614b44d85c3c87e188d3cc78ef0442ce5032ee6818de20a0162ba1074725c0d08908f62ea979227 - languageName: node - linkType: hard - "buffer-from@npm:^1.0.0": version: 1.1.2 resolution: "buffer-from@npm:1.1.2" @@ -3439,13 +2604,6 @@ __metadata: languageName: node linkType: hard -"callsites@npm:^3.1.0": - version: 3.1.0 - resolution: "callsites@npm:3.1.0" - checksum: 10c0/fff92277400eb06c3079f9e74f3af120db9f8ea03bad0e84d9aede54bbe2d44a56cccb5f6cf12211f93f52306df87077ecec5b712794c5a9b5dac6d615a3f301 - languageName: node - linkType: hard - "camel-case@npm:^4.1.2": version: 4.1.2 resolution: "camel-case@npm:4.1.2" @@ -3456,20 +2614,6 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^5.3.1": - version: 5.3.1 - resolution: "camelcase@npm:5.3.1" - checksum: 10c0/92ff9b443bfe8abb15f2b1513ca182d16126359ad4f955ebc83dc4ddcc4ef3fdd2c078bc223f2673dc223488e75c99b16cc4d056624374b799e6a1555cf61b23 - languageName: node - linkType: hard - -"camelcase@npm:^6.3.0": - version: 6.3.0 - resolution: "camelcase@npm:6.3.0" - checksum: 10c0/0d701658219bd3116d12da3eab31acddb3f9440790c0792e0d398f0a520a6a4058018e546862b6fba89d7ae990efaeb97da71e1913e9ebf5a8b5621a3d55c710 - languageName: node - linkType: hard - "caniuse-lite@npm:^1.0.30001782": version: 1.0.30001791 resolution: "caniuse-lite@npm:1.0.30001791" @@ -3495,16 +2639,6 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.1.2": - version: 4.1.2 - resolution: "chalk@npm:4.1.2" - dependencies: - ansi-styles: "npm:^4.1.0" - supports-color: "npm:^7.1.0" - checksum: 10c0/4a3fef5cc34975c898ffe77141450f679721df9dde00f6c304353fa9c8b571929123b26a0e4617bde5018977eb655b31970c297b91b63ee83bb82aeb04666880 - languageName: node - linkType: hard - "change-case@npm:^4.1.2": version: 4.1.2 resolution: "change-case@npm:4.1.2" @@ -3525,13 +2659,6 @@ __metadata: languageName: node linkType: hard -"char-regex@npm:^1.0.2": - version: 1.0.2 - resolution: "char-regex@npm:1.0.2" - checksum: 10c0/57a09a86371331e0be35d9083ba429e86c4f4648ecbe27455dbfb343037c16ee6fdc7f6b61f433a57cc5ded5561d71c56a150e018f40c2ffb7bc93a26dae341e - languageName: node - linkType: hard - "chownr@npm:^3.0.0": version: 3.0.0 resolution: "chownr@npm:3.0.0" @@ -3539,20 +2666,6 @@ __metadata: languageName: node linkType: hard -"ci-info@npm:^4.2.0": - version: 4.4.0 - resolution: "ci-info@npm:4.4.0" - checksum: 10c0/44156201545b8dde01aa8a09ee2fe9fc7a73b1bef9adbd4606c9f61c8caeeb73fb7a575c88b0443f7b4edb5ee45debaa59ed54ba5f99698339393ca01349eb3a - languageName: node - linkType: hard - -"cjs-module-lexer@npm:^2.1.0": - version: 2.2.0 - resolution: "cjs-module-lexer@npm:2.2.0" - checksum: 10c0/aec4ca58f87145fac221386790ecaae8b012f2e2359a45acb61d8c75ea4fa84f6ea869f17abc1a7e91a808eff0fed581209632f03540de16f72f0a28f5fd35ac - languageName: node - linkType: hard - "classnames@npm:^2.3.1, classnames@npm:^2.3.2": version: 2.5.1 resolution: "classnames@npm:2.5.1" @@ -3560,47 +2673,6 @@ __metadata: languageName: node linkType: hard -"cliui@npm:^8.0.1": - version: 8.0.1 - resolution: "cliui@npm:8.0.1" - dependencies: - string-width: "npm:^4.2.0" - strip-ansi: "npm:^6.0.1" - wrap-ansi: "npm:^7.0.0" - checksum: 10c0/4bda0f09c340cbb6dfdc1ed508b3ca080f12992c18d68c6be4d9cf51756033d5266e61ec57529e610dacbf4da1c634423b0c1b11037709cc6b09045cbd815df5 - languageName: node - linkType: hard - -"co@npm:^4.6.0": - version: 4.6.0 - resolution: "co@npm:4.6.0" - checksum: 10c0/c0e85ea0ca8bf0a50cbdca82efc5af0301240ca88ebe3644a6ffb8ffe911f34d40f8fbcf8f1d52c5ddd66706abd4d3bfcd64259f1e8e2371d4f47573b0dc8c28 - languageName: node - linkType: hard - -"collect-v8-coverage@npm:^1.0.2": - version: 1.0.3 - resolution: "collect-v8-coverage@npm:1.0.3" - checksum: 10c0/bc62ba251bcce5e3354a8f88fa6442bee56e3e612fec08d4dfcf66179b41ea0bf544b0f78c4ebc0f8050871220af95bb5c5578a6aef346feea155640582f09dc - languageName: node - linkType: hard - -"color-convert@npm:^2.0.1": - version: 2.0.1 - resolution: "color-convert@npm:2.0.1" - dependencies: - color-name: "npm:~1.1.4" - checksum: 10c0/37e1150172f2e311fe1b2df62c6293a342ee7380da7b9cfdba67ea539909afbd74da27033208d01d6d5cfc65ee7868a22e18d7e7648e004425441c0f8a15a7d7 - languageName: node - linkType: hard - -"color-name@npm:~1.1.4": - version: 1.1.4 - resolution: "color-name@npm:1.1.4" - checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95 - languageName: node - linkType: hard - "commander@npm:^15.0.0": version: 15.0.0 resolution: "commander@npm:15.0.0" @@ -3636,13 +2708,6 @@ __metadata: languageName: node linkType: hard -"concat-map@npm:0.0.1": - version: 0.0.1 - resolution: "concat-map@npm:0.0.1" - checksum: 10c0/c996b1cfdf95b6c90fee4dae37e332c8b6eb7d106430c17d538034c0ad9a1630cb194d2ab37293b1bdd4d779494beee7786d586a50bd9376fd6f7bcc2bd4c98f - languageName: node - linkType: hard - "constant-case@npm:^3.0.4": version: 3.0.4 resolution: "constant-case@npm:3.0.4" @@ -3661,7 +2726,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6": +"cross-spawn@npm:^7.0.6": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" dependencies: @@ -3679,7 +2744,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.4.1, debug@npm:^4.4.3": +"debug@npm:^4.1.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.4.1, debug@npm:^4.4.3": version: 4.4.3 resolution: "debug@npm:4.4.3" dependencies: @@ -3698,18 +2763,6 @@ __metadata: languageName: node linkType: hard -"dedent@npm:^1.6.0": - version: 1.7.2 - resolution: "dedent@npm:1.7.2" - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true - checksum: 10c0/acaff07cac355b93f17b1b17ebbb84d3cc55af6ab4b7814c3f505e061903e168bc6bf9ddce331552d64dee1525f0b4c549c9ade46aebfac6f69caaed74e90751 - languageName: node - linkType: hard - "deep-is@npm:^0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" @@ -3717,7 +2770,7 @@ __metadata: languageName: node linkType: hard -"deepmerge@npm:^4.2.2, deepmerge@npm:^4.3.1": +"deepmerge@npm:^4.2.2": version: 4.3.1 resolution: "deepmerge@npm:4.3.1" checksum: 10c0/e53481aaf1aa2c4082b5342be6b6d8ad9dfe387bc92ce197a66dea08bd4265904a087e75e464f14d1347cf2ac8afe1e4c16b266e0561cc5df29382d3c5f80044 @@ -3731,13 +2784,6 @@ __metadata: languageName: node linkType: hard -"detect-newline@npm:^3.1.0": - version: 3.1.0 - resolution: "detect-newline@npm:3.1.0" - checksum: 10c0/c38cfc8eeb9fda09febb44bcd85e467c970d4e3bf526095394e5a4f18bc26dd0cf6b22c69c1fa9969261521c593836db335c2795218f6d781a512aea2fb8209d - languageName: node - linkType: hard - "discontinuous-range@npm:1.0.0": version: 1.0.0 resolution: "discontinuous-range@npm:1.0.0" @@ -3765,13 +2811,6 @@ __metadata: languageName: node linkType: hard -"eastasianwidth@npm:^0.2.0": - version: 0.2.0 - resolution: "eastasianwidth@npm:0.2.0" - checksum: 10c0/26f364ebcdb6395f95124fda411f63137a4bfb5d3a06453f7f23dfe52502905bd84e0488172e0f9ec295fdc45f05c23d5d91baf16bd26f0fe9acd777a188dc39 - languageName: node - linkType: hard - "electron-to-chromium@npm:^1.5.328": version: 1.5.349 resolution: "electron-to-chromium@npm:1.5.349" @@ -3779,27 +2818,6 @@ __metadata: languageName: node linkType: hard -"emittery@npm:^0.13.1": - version: 0.13.1 - resolution: "emittery@npm:0.13.1" - checksum: 10c0/1573d0ae29ab34661b6c63251ff8f5facd24ccf6a823f19417ae8ba8c88ea450325788c67f16c99edec8de4b52ce93a10fe441ece389fd156e88ee7dab9bfa35 - languageName: node - linkType: hard - -"emoji-regex@npm:^8.0.0": - version: 8.0.0 - resolution: "emoji-regex@npm:8.0.0" - checksum: 10c0/b6053ad39951c4cf338f9092d7bfba448cdfd46fe6a2a034700b149ac9ffbc137e361cbd3c442297f86bed2e5f7576c1b54cc0a6bf8ef5106cc62f496af35010 - languageName: node - linkType: hard - -"emoji-regex@npm:^9.2.2": - version: 9.2.2 - resolution: "emoji-regex@npm:9.2.2" - checksum: 10c0/af014e759a72064cf66e6e694a7fc6b0ed3d8db680427b021a89727689671cefe9d04151b2cad51dbaf85d5ba790d061cd167f1cf32eb7b281f6368b3c181639 - languageName: node - linkType: hard - "empathic@npm:^2.0.1": version: 2.1.0 resolution: "empathic@npm:2.1.0" @@ -3814,15 +2832,6 @@ __metadata: languageName: node linkType: hard -"error-ex@npm:^1.3.1": - version: 1.3.4 - resolution: "error-ex@npm:1.3.4" - dependencies: - is-arrayish: "npm:^0.2.1" - checksum: 10c0/b9e34ff4778b8f3b31a8377e1c654456f4c41aeaa3d10a1138c3b7635d8b7b2e03eb2475d46d8ae055c1f180a1063e100bffabf64ea7e7388b37735df5328664 - languageName: node - linkType: hard - "es-errors@npm:^1.3.0": version: 1.3.0 resolution: "es-errors@npm:1.3.0" @@ -3837,7 +2846,7 @@ __metadata: languageName: node linkType: hard -"escalade@npm:^3.1.1, escalade@npm:^3.2.0": +"escalade@npm:^3.2.0": version: 3.2.0 resolution: "escalade@npm:3.2.0" checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65 @@ -3851,13 +2860,6 @@ __metadata: languageName: node linkType: hard -"escape-string-regexp@npm:^2.0.0": - version: 2.0.0 - resolution: "escape-string-regexp@npm:2.0.0" - checksum: 10c0/2530479fe8db57eace5e8646c9c2a9c80fa279614986d16dcc6bcaceb63ae77f05a851ba6c43756d816c61d7f4534baf56e3c705e3e0d884818a46808811c507 - languageName: node - linkType: hard - "escape-string-regexp@npm:^4.0.0": version: 4.0.0 resolution: "escape-string-regexp@npm:4.0.0" @@ -3999,16 +3001,6 @@ __metadata: languageName: node linkType: hard -"esprima@npm:^4.0.0": - version: 4.0.1 - resolution: "esprima@npm:4.0.1" - bin: - esparse: ./bin/esparse.js - esvalidate: ./bin/esvalidate.js - checksum: 10c0/ad4bab9ead0808cf56501750fd9d3fb276f6b105f987707d059005d57e182d18a7c9ec7f3a01794ebddcca676773e42ca48a32d67a250c9d35e009ca613caba3 - languageName: node - linkType: hard - "esquery@npm:^1.7.0": version: 1.7.0 resolution: "esquery@npm:1.7.0" @@ -4057,30 +3049,6 @@ __metadata: languageName: node linkType: hard -"execa@npm:^5.1.1": - version: 5.1.1 - resolution: "execa@npm:5.1.1" - dependencies: - cross-spawn: "npm:^7.0.3" - get-stream: "npm:^6.0.0" - human-signals: "npm:^2.1.0" - is-stream: "npm:^2.0.0" - merge-stream: "npm:^2.0.0" - npm-run-path: "npm:^4.0.1" - onetime: "npm:^5.1.2" - signal-exit: "npm:^3.0.3" - strip-final-newline: "npm:^2.0.0" - checksum: 10c0/c8e615235e8de4c5addf2fa4c3da3e3aa59ce975a3e83533b4f6a71750fb816a2e79610dc5f1799b6e28976c9ae86747a36a606655bf8cb414a74d8d507b304f - languageName: node - linkType: hard - -"exit-x@npm:^0.2.2": - version: 0.2.2 - resolution: "exit-x@npm:0.2.2" - checksum: 10c0/212a7a095ca5540e9581f1ef2d1d6a40df7a6027c8cc96e78ce1d16b86d1a88326d4a0eff8dff2b5ec1e68bb0c1edd5d0dfdde87df1869bf7514d4bc6a5cbd72 - languageName: node - linkType: hard - "expect-type@npm:^1.4.0": version: 1.4.0 resolution: "expect-type@npm:1.4.0" @@ -4088,20 +3056,6 @@ __metadata: languageName: node linkType: hard -"expect@npm:30.4.1, expect@npm:^30.0.0": - version: 30.4.1 - resolution: "expect@npm:30.4.1" - dependencies: - "@jest/expect-utils": "npm:30.4.1" - "@jest/get-type": "npm:30.1.0" - jest-matcher-utils: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-mock: "npm:30.4.1" - jest-util: "npm:30.4.1" - checksum: 10c0/ad04fbdffac5a2bae186478938a60f737e3aac823db9a80c87f3f390f9f458bddcc454dc3a3997d715706747c6aff928923e6a71db3a221adb89a51cc1582e72 - languageName: node - linkType: hard - "exponential-backoff@npm:^3.1.1": version: 3.1.3 resolution: "exponential-backoff@npm:3.1.3" @@ -4116,7 +3070,7 @@ __metadata: languageName: node linkType: hard -"fast-json-stable-stringify@npm:2.x, fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0": +"fast-json-stable-stringify@npm:^2.0.0": version: 2.1.0 resolution: "fast-json-stable-stringify@npm:2.1.0" checksum: 10c0/7f081eb0b8a64e0057b3bb03f974b3ef00135fbf36c1c710895cd9300f13c94ba809bb3a81cf4e1b03f6e5285610a61abbd7602d0652de423144dfee5a389c9b @@ -4171,15 +3125,6 @@ __metadata: languageName: node linkType: hard -"fb-watchman@npm:^2.0.2": - version: 2.0.2 - resolution: "fb-watchman@npm:2.0.2" - dependencies: - bser: "npm:2.1.1" - checksum: 10c0/feae89ac148adb8f6ae8ccd87632e62b13563e6fb114cacb5265c51f585b17e2e268084519fb2edd133872f1d47a18e6bfd7e5e08625c0d41b93149694187581 - languageName: node - linkType: hard - "fdir@npm:^6.2.0, fdir@npm:^6.5.0": version: 6.5.0 resolution: "fdir@npm:6.5.0" @@ -4201,16 +3146,6 @@ __metadata: languageName: node linkType: hard -"find-up@npm:^4.0.0, find-up@npm:^4.1.0": - version: 4.1.0 - resolution: "find-up@npm:4.1.0" - dependencies: - locate-path: "npm:^5.0.0" - path-exists: "npm:^4.0.0" - checksum: 10c0/0406ee89ebeefa2d507feb07ec366bebd8a6167ae74aa4e34fb4c4abd06cf782a3ce26ae4194d70706f72182841733f00551c209fe575cb00bd92104056e78c1 - languageName: node - linkType: hard - "find-up@npm:^5.0.0": version: 5.0.0 resolution: "find-up@npm:5.0.0" @@ -4245,16 +3180,6 @@ __metadata: languageName: node linkType: hard -"foreground-child@npm:^3.1.0": - version: 3.3.1 - resolution: "foreground-child@npm:3.3.1" - dependencies: - cross-spawn: "npm:^7.0.6" - signal-exit: "npm:^4.0.1" - checksum: 10c0/8986e4af2430896e65bc2788d6679067294d6aee9545daefc84923a0a4b399ad9c7a3ea7bd8c0b2b80fdf4a92de4c69df3f628233ff3224260e9c1541a9e9ed3 - languageName: node - linkType: hard - "fraction.js@npm:^5.2.1": version: 5.3.4 resolution: "fraction.js@npm:5.3.4" @@ -4262,14 +3187,7 @@ __metadata: languageName: node linkType: hard -"fs.realpath@npm:^1.0.0": - version: 1.0.0 - resolution: "fs.realpath@npm:1.0.0" - checksum: 10c0/444cf1291d997165dfd4c0d58b69f0e4782bfd9149fd72faa4fe299e68e0e93d6db941660b37dd29153bf7186672ececa3b50b7e7249477b03fdf850f287c948 - languageName: node - linkType: hard - -"fsevents@npm:^2.3.3, fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": +"fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": version: 2.3.3 resolution: "fsevents@npm:2.3.3" dependencies: @@ -4279,7 +3197,7 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@npm%3A^2.3.3#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": +"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": version: 2.3.3 resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" dependencies: @@ -4302,27 +3220,6 @@ __metadata: languageName: node linkType: hard -"get-caller-file@npm:^2.0.5": - version: 2.0.5 - resolution: "get-caller-file@npm:2.0.5" - checksum: 10c0/c6c7b60271931fa752aeb92f2b47e355eac1af3a2673f47c9589e8f8a41adc74d45551c1bc57b5e66a80609f10ffb72b6f575e4370d61cc3f7f3aaff01757cde - languageName: node - linkType: hard - -"get-package-type@npm:^0.1.0": - version: 0.1.0 - resolution: "get-package-type@npm:0.1.0" - checksum: 10c0/e34cdf447fdf1902a1f6d5af737eaadf606d2ee3518287abde8910e04159368c268568174b2e71102b87b26c2020486f126bfca9c4fb1ceb986ff99b52ecd1be - languageName: node - linkType: hard - -"get-stream@npm:^6.0.0": - version: 6.0.1 - resolution: "get-stream@npm:6.0.1" - checksum: 10c0/49825d57d3fd6964228e6200a58169464b8e8970489b3acdc24906c782fb7f01f9f56f8e6653c4a50713771d6658f7cfe051e5eb8c12e334138c9c918b296341 - languageName: node - linkType: hard - "get-tsconfig@npm:^4.10.1": version: 4.14.1 resolution: "get-tsconfig@npm:4.14.1" @@ -4341,61 +3238,13 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.5.0": - version: 10.5.0 - resolution: "glob@npm:10.5.0" - dependencies: - foreground-child: "npm:^3.1.0" - jackspeak: "npm:^3.1.2" - minimatch: "npm:^9.0.4" - minipass: "npm:^7.1.2" - package-json-from-dist: "npm:^1.0.0" - path-scurry: "npm:^1.11.1" - bin: - glob: dist/esm/bin.mjs - checksum: 10c0/100705eddbde6323e7b35e1d1ac28bcb58322095bd8e63a7d0bef1a2cdafe0d0f7922a981b2b48369a4f8c1b077be5c171804534c3509dfe950dde15fbe6d828 - languageName: node - linkType: hard - -"glob@npm:^7.1.4": - version: 7.2.3 - resolution: "glob@npm:7.2.3" - dependencies: - fs.realpath: "npm:^1.0.0" - inflight: "npm:^1.0.4" - inherits: "npm:2" - minimatch: "npm:^3.1.1" - once: "npm:^1.3.0" - path-is-absolute: "npm:^1.0.0" - checksum: 10c0/65676153e2b0c9095100fe7f25a778bf45608eeb32c6048cf307f579649bcc30353277b3b898a3792602c65764e5baa4f643714dfbdfd64ea271d210c7a425fe - languageName: node - linkType: hard - -"graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.6": +"graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 languageName: node linkType: hard -"handlebars@npm:^4.7.9": - version: 4.7.10 - resolution: "handlebars@npm:4.7.10" - dependencies: - minimist: "npm:^1.2.8" - neo-async: "npm:^2.6.2" - source-map: "npm:^0.6.1" - uglify-js: "npm:^3.1.4" - wordwrap: "npm:^1.0.0" - dependenciesMeta: - uglify-js: - optional: true - bin: - handlebars: bin/handlebars - checksum: 10c0/9b5573584ae5836172ae3f1502d9819568ba1b92ee83b81b18223beeb4c07486595e7b0fa633102beabc66ed06fe6e7da1dd9a9ea8c7072b9550b9a88809165c - languageName: node - linkType: hard - "has-flag@npm:^4.0.0": version: 4.0.0 resolution: "has-flag@npm:4.0.0" @@ -4438,13 +3287,6 @@ __metadata: languageName: node linkType: hard -"human-signals@npm:^2.1.0": - version: 2.1.0 - resolution: "human-signals@npm:2.1.0" - checksum: 10c0/695edb3edfcfe9c8b52a76926cd31b36978782062c0ed9b1192b36bebc75c4c87c82e178dfcb0ed0fc27ca59d434198aac0bd0be18f5781ded775604db22304a - languageName: node - linkType: hard - "ignore@npm:^5.2.0": version: 5.3.2 resolution: "ignore@npm:5.3.2" @@ -4459,18 +3301,6 @@ __metadata: languageName: node linkType: hard -"import-local@npm:^3.2.0": - version: 3.2.0 - resolution: "import-local@npm:3.2.0" - dependencies: - pkg-dir: "npm:^4.2.0" - resolve-cwd: "npm:^3.0.0" - bin: - import-local-fixture: fixtures/cli.js - checksum: 10c0/94cd6367a672b7e0cb026970c85b76902d2710a64896fa6de93bd5c571dd03b228c5759308959de205083e3b1c61e799f019c9e36ee8e9c523b993e1057f0433 - languageName: node - linkType: hard - "import-meta-resolve@npm:^4.2.0": version: 4.2.0 resolution: "import-meta-resolve@npm:4.2.0" @@ -4485,30 +3315,6 @@ __metadata: languageName: node linkType: hard -"inflight@npm:^1.0.4": - version: 1.0.6 - resolution: "inflight@npm:1.0.6" - dependencies: - once: "npm:^1.3.0" - wrappy: "npm:1" - checksum: 10c0/7faca22584600a9dc5b9fca2cd5feb7135ac8c935449837b315676b4c90aa4f391ec4f42240178244b5a34e8bede1948627fda392ca3191522fc46b34e985ab2 - languageName: node - linkType: hard - -"inherits@npm:2": - version: 2.0.4 - resolution: "inherits@npm:2.0.4" - checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 - languageName: node - linkType: hard - -"is-arrayish@npm:^0.2.1": - version: 0.2.1 - resolution: "is-arrayish@npm:0.2.1" - checksum: 10c0/e7fb686a739068bb70f860b39b67afc62acc62e36bb61c5f965768abce1873b379c563e61dd2adad96ebb7edf6651111b385e490cf508378959b0ed4cac4e729 - languageName: node - linkType: hard - "is-core-module@npm:^2.16.1": version: 2.16.1 resolution: "is-core-module@npm:2.16.1" @@ -4525,20 +3331,6 @@ __metadata: languageName: node linkType: hard -"is-fullwidth-code-point@npm:^3.0.0": - version: 3.0.0 - resolution: "is-fullwidth-code-point@npm:3.0.0" - checksum: 10c0/bb11d825e049f38e04c06373a8d72782eee0205bda9d908cc550ccb3c59b99d750ff9537982e01733c1c94a58e35400661f57042158ff5e8f3e90cf936daf0fc - languageName: node - linkType: hard - -"is-generator-fn@npm:^2.1.0": - version: 2.1.0 - resolution: "is-generator-fn@npm:2.1.0" - checksum: 10c0/2957cab387997a466cd0bf5c1b6047bd21ecb32bdcfd8996b15747aa01002c1c88731802f1b3d34ac99f4f6874b626418bd118658cf39380fe5fff32a3af9c4d - languageName: node - linkType: hard - "is-glob@npm:^4.0.0, is-glob@npm:^4.0.3": version: 4.0.3 resolution: "is-glob@npm:4.0.3" @@ -4564,13 +3356,6 @@ __metadata: languageName: node linkType: hard -"is-stream@npm:^2.0.0": - version: 2.0.1 - resolution: "is-stream@npm:2.0.1" - checksum: 10c0/7c284241313fc6efc329b8d7f08e16c0efeb6baab1b4cd0ba579eb78e5af1aa5da11e68559896a2067cd6c526bd29241dda4eb1225e627d5aa1a89a76d4635a5 - languageName: node - linkType: hard - "isexe@npm:^2.0.0": version: 2.0.0 resolution: "isexe@npm:2.0.0" @@ -4585,26 +3370,13 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0, istanbul-lib-coverage@npm:^3.2.2": +"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.2": version: 3.2.2 resolution: "istanbul-lib-coverage@npm:3.2.2" checksum: 10c0/6c7ff2106769e5f592ded1fb418f9f73b4411fd5a084387a5410538332b6567cd1763ff6b6cadca9b9eb2c443cce2f7ea7d7f1b8d315f9ce58539793b1e0922b languageName: node linkType: hard -"istanbul-lib-instrument@npm:^6.0.0, istanbul-lib-instrument@npm:^6.0.2": - version: 6.0.3 - resolution: "istanbul-lib-instrument@npm:6.0.3" - dependencies: - "@babel/core": "npm:^7.23.9" - "@babel/parser": "npm:^7.23.9" - "@istanbuljs/schema": "npm:^0.1.3" - istanbul-lib-coverage: "npm:^3.2.0" - semver: "npm:^7.5.4" - checksum: 10c0/a1894e060dd2a3b9f046ffdc87b44c00a35516f5e6b7baf4910369acca79e506fc5323a816f811ae23d82334b38e3ddeb8b3b331bd2c860540793b59a8689128 - languageName: node - linkType: hard - "istanbul-lib-report@npm:^3.0.0, istanbul-lib-report@npm:^3.0.1": version: 3.0.1 resolution: "istanbul-lib-report@npm:3.0.1" @@ -4616,18 +3388,7 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-source-maps@npm:^5.0.0": - version: 5.0.6 - resolution: "istanbul-lib-source-maps@npm:5.0.6" - dependencies: - "@jridgewell/trace-mapping": "npm:^0.3.23" - debug: "npm:^4.1.1" - istanbul-lib-coverage: "npm:^3.0.0" - checksum: 10c0/ffe75d70b303a3621ee4671554f306e0831b16f39ab7f4ab52e54d356a5d33e534d97563e318f1333a6aae1d42f91ec49c76b6cd3f3fb378addcb5c81da0255f - languageName: node - linkType: hard - -"istanbul-reports@npm:^3.1.3, istanbul-reports@npm:^3.2.0": +"istanbul-reports@npm:^3.2.0": version: 3.2.0 resolution: "istanbul-reports@npm:3.2.0" dependencies: @@ -4648,19 +3409,6 @@ __metadata: languageName: node linkType: hard -"jackspeak@npm:^3.1.2": - version: 3.4.3 - resolution: "jackspeak@npm:3.4.3" - dependencies: - "@isaacs/cliui": "npm:^8.0.2" - "@pkgjs/parseargs": "npm:^0.11.0" - dependenciesMeta: - "@pkgjs/parseargs": - optional: true - checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9 - languageName: node - linkType: hard - "javascript-natural-sort@npm:^0.7.1": version: 0.7.1 resolution: "javascript-natural-sort@npm:0.7.1" @@ -4668,444 +3416,6 @@ __metadata: languageName: node linkType: hard -"jest-changed-files@npm:30.4.1": - version: 30.4.1 - resolution: "jest-changed-files@npm:30.4.1" - dependencies: - execa: "npm:^5.1.1" - jest-util: "npm:30.4.1" - p-limit: "npm:^3.1.0" - checksum: 10c0/324bbec3920a7d9ceb1d11872b9f1befe73d152a7ef289243f663bf3b22afe124c2c656ec316e44393f30a83b74a1738b56307a066906fa49b800686fd4d0f04 - languageName: node - linkType: hard - -"jest-circus@npm:30.4.2": - version: 30.4.2 - resolution: "jest-circus@npm:30.4.2" - dependencies: - "@jest/environment": "npm:30.4.1" - "@jest/expect": "npm:30.4.1" - "@jest/test-result": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - chalk: "npm:^4.1.2" - co: "npm:^4.6.0" - dedent: "npm:^1.6.0" - is-generator-fn: "npm:^2.1.0" - jest-each: "npm:30.4.1" - jest-matcher-utils: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-runtime: "npm:30.4.2" - jest-snapshot: "npm:30.4.1" - jest-util: "npm:30.4.1" - p-limit: "npm:^3.1.0" - pretty-format: "npm:30.4.1" - pure-rand: "npm:^7.0.0" - slash: "npm:^3.0.0" - stack-utils: "npm:^2.0.6" - checksum: 10c0/5d99f1336eb249057063a007fabad4ced802501fbaad7ddeea8db9553fa54fbd44d26e71e8bf61a0979d42b3b93a3d920e6f00afa26cdbb70d1e7d0969515d10 - languageName: node - linkType: hard - -"jest-cli@npm:30.4.2": - version: 30.4.2 - resolution: "jest-cli@npm:30.4.2" - dependencies: - "@jest/core": "npm:30.4.2" - "@jest/test-result": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - chalk: "npm:^4.1.2" - exit-x: "npm:^0.2.2" - import-local: "npm:^3.2.0" - jest-config: "npm:30.4.2" - jest-util: "npm:30.4.1" - jest-validate: "npm:30.4.1" - yargs: "npm:^17.7.2" - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - bin: - jest: ./bin/jest.js - checksum: 10c0/a036a1bf06ce7d5fed644a518c4a4ccf60c5fe5f3d96d143973048e6690c4a28a4f97fa3275d90ca236430a1b2a7c10544e7e190a4f2edfdf0a4e6daf1f6a384 - languageName: node - linkType: hard - -"jest-config@npm:30.4.2": - version: 30.4.2 - resolution: "jest-config@npm:30.4.2" - dependencies: - "@babel/core": "npm:^7.27.4" - "@jest/get-type": "npm:30.1.0" - "@jest/pattern": "npm:30.4.0" - "@jest/test-sequencer": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - babel-jest: "npm:30.4.1" - chalk: "npm:^4.1.2" - ci-info: "npm:^4.2.0" - deepmerge: "npm:^4.3.1" - glob: "npm:^10.5.0" - graceful-fs: "npm:^4.2.11" - jest-circus: "npm:30.4.2" - jest-docblock: "npm:30.4.0" - jest-environment-node: "npm:30.4.1" - jest-regex-util: "npm:30.4.0" - jest-resolve: "npm:30.4.1" - jest-runner: "npm:30.4.2" - jest-util: "npm:30.4.1" - jest-validate: "npm:30.4.1" - parse-json: "npm:^5.2.0" - pretty-format: "npm:30.4.1" - slash: "npm:^3.0.0" - strip-json-comments: "npm:^3.1.1" - peerDependencies: - "@types/node": "*" - esbuild-register: ">=3.4.0" - ts-node: ">=9.0.0" - peerDependenciesMeta: - "@types/node": - optional: true - esbuild-register: - optional: true - ts-node: - optional: true - checksum: 10c0/18300b1dc54a4bfb5d1db6c10aeb01b6c64736224e3f60d119da9504d49cbab5a76d789f38c44af7d168418463356db6843ad7e44f249c63ce7f409758eba0c6 - languageName: node - linkType: hard - -"jest-diff@npm:30.4.1": - version: 30.4.1 - resolution: "jest-diff@npm:30.4.1" - dependencies: - "@jest/diff-sequences": "npm:30.4.0" - "@jest/get-type": "npm:30.1.0" - chalk: "npm:^4.1.2" - pretty-format: "npm:30.4.1" - checksum: 10c0/787e11f0ea27e94815479d6c5415e4173da1e74bede34c1515b8515fc9d1fe053e2ad25a3c31f9998a7292c186a0e4d395ed82e0e149d57d7708ee6759b442e9 - languageName: node - linkType: hard - -"jest-docblock@npm:30.4.0": - version: 30.4.0 - resolution: "jest-docblock@npm:30.4.0" - dependencies: - detect-newline: "npm:^3.1.0" - checksum: 10c0/1fe1c971207e1b905e4f23d98e508a03ae631337e9ffa347ff2f6df81a1d75ced7ed3e52a809fad75fb8a8cd55b6bda4483bc124e5e1d7529eeb4ef76b29e913 - languageName: node - linkType: hard - -"jest-each@npm:30.4.1": - version: 30.4.1 - resolution: "jest-each@npm:30.4.1" - dependencies: - "@jest/get-type": "npm:30.1.0" - "@jest/types": "npm:30.4.1" - chalk: "npm:^4.1.2" - jest-util: "npm:30.4.1" - pretty-format: "npm:30.4.1" - checksum: 10c0/41bc1cec23901cb0c7d8f547a70574fffca8cc16a1660ed97645bf3b61f4e6151aaa58bb14ce55a3cd9f5a63a2cc782a39366caf3304a2159d1e3cc5ae79a9e4 - languageName: node - linkType: hard - -"jest-environment-node@npm:30.4.1": - version: 30.4.1 - resolution: "jest-environment-node@npm:30.4.1" - dependencies: - "@jest/environment": "npm:30.4.1" - "@jest/fake-timers": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - jest-mock: "npm:30.4.1" - jest-util: "npm:30.4.1" - jest-validate: "npm:30.4.1" - checksum: 10c0/d8d6bb22bfd280f077b5856558d9d7112c48fd3bae6eda9b76694f1c8e1be783a725686a137437d180c9d49e6b37386c8e342e0b8e5bfcb6526dee9c10cc31ec - languageName: node - linkType: hard - -"jest-haste-map@npm:30.4.1": - version: 30.4.1 - resolution: "jest-haste-map@npm:30.4.1" - dependencies: - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - anymatch: "npm:^3.1.3" - fb-watchman: "npm:^2.0.2" - fsevents: "npm:^2.3.3" - graceful-fs: "npm:^4.2.11" - jest-regex-util: "npm:30.4.0" - jest-util: "npm:30.4.1" - jest-worker: "npm:30.4.1" - picomatch: "npm:^4.0.3" - walker: "npm:^1.0.8" - dependenciesMeta: - fsevents: - optional: true - checksum: 10c0/1350c24952bbf31c86cb1ed4e2e5edd4766a93e2be8816c4648c05463d06cfae89f3c73732f9274fdb626fdfdfe6605ed6f259b6c21257df536a6379d4b9a5e7 - languageName: node - linkType: hard - -"jest-leak-detector@npm:30.4.1": - version: 30.4.1 - resolution: "jest-leak-detector@npm:30.4.1" - dependencies: - "@jest/get-type": "npm:30.1.0" - pretty-format: "npm:30.4.1" - checksum: 10c0/57256ac08f12186e3ed1687126b8d75a12de9c4ffa959ff41322e9ba5f93e3ed8af91dc36bc4d59f77cef6d4008bcf5a3e646cdd950743898576aec8dbae6778 - languageName: node - linkType: hard - -"jest-matcher-utils@npm:30.4.1": - version: 30.4.1 - resolution: "jest-matcher-utils@npm:30.4.1" - dependencies: - "@jest/get-type": "npm:30.1.0" - chalk: "npm:^4.1.2" - jest-diff: "npm:30.4.1" - pretty-format: "npm:30.4.1" - checksum: 10c0/ddbb0c7075def27ba30160883c327cb3fd13f561f5789d00a1edca1b48b0651f8ea23a1c51bcfcb6413a68c47d658bcf47a34701b8a39ce135dd28d87a3117af - languageName: node - linkType: hard - -"jest-message-util@npm:30.4.1": - version: 30.4.1 - resolution: "jest-message-util@npm:30.4.1" - dependencies: - "@babel/code-frame": "npm:^7.27.1" - "@jest/types": "npm:30.4.1" - "@types/stack-utils": "npm:^2.0.3" - chalk: "npm:^4.1.2" - graceful-fs: "npm:^4.2.11" - jest-util: "npm:30.4.1" - picomatch: "npm:^4.0.3" - pretty-format: "npm:30.4.1" - slash: "npm:^3.0.0" - stack-utils: "npm:^2.0.6" - checksum: 10c0/ae7427544e042bc1c14abf3c0dbe8b83d0dbec22a9a5efefaca5b8ccb6b9bf391abe732e6f2117ca995c6889bfe1be35c78cec75e5ea0a50e28cffe1ba6f9fdf - languageName: node - linkType: hard - -"jest-mock@npm:30.4.1": - version: 30.4.1 - resolution: "jest-mock@npm:30.4.1" - dependencies: - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - jest-util: "npm:30.4.1" - checksum: 10c0/5185a41255285c1634c5d85dda037afaaadfc12793b3293c9e253a30bb67449f8df968447f830abb9cf7a52e63694e6734680130e8085ce119056280890bf6fc - languageName: node - linkType: hard - -"jest-pnp-resolver@npm:^1.2.3": - version: 1.2.3 - resolution: "jest-pnp-resolver@npm:1.2.3" - peerDependencies: - jest-resolve: "*" - peerDependenciesMeta: - jest-resolve: - optional: true - checksum: 10c0/86eec0c78449a2de733a6d3e316d49461af6a858070e113c97f75fb742a48c2396ea94150cbca44159ffd4a959f743a47a8b37a792ef6fdad2cf0a5cba973fac - languageName: node - linkType: hard - -"jest-regex-util@npm:30.4.0": - version: 30.4.0 - resolution: "jest-regex-util@npm:30.4.0" - checksum: 10c0/fe7426f67b54d38bed8e9d6e6a099d63d72f41f5bf65b922d9d03fedcb55c614b45657207632f6ee22d0a59d8d11327891f258d23f68a58912fcdb0f7db48435 - languageName: node - linkType: hard - -"jest-resolve-dependencies@npm:30.4.2": - version: 30.4.2 - resolution: "jest-resolve-dependencies@npm:30.4.2" - dependencies: - jest-regex-util: "npm:30.4.0" - jest-snapshot: "npm:30.4.1" - checksum: 10c0/4101afabd2a4ef4e6c82bf82ea145286c1238373f7611938e8d47ddcf5aaa6e10af365436a934b7af194451e351774829cb021ac73f857b4873dcccc7aabb616 - languageName: node - linkType: hard - -"jest-resolve@npm:30.4.1": - version: 30.4.1 - resolution: "jest-resolve@npm:30.4.1" - dependencies: - chalk: "npm:^4.1.2" - graceful-fs: "npm:^4.2.11" - jest-haste-map: "npm:30.4.1" - jest-pnp-resolver: "npm:^1.2.3" - jest-util: "npm:30.4.1" - jest-validate: "npm:30.4.1" - slash: "npm:^3.0.0" - unrs-resolver: "npm:^1.7.11" - checksum: 10c0/0a99ef4f4fd7b3678d58a5e1cf8f0b5ec1997cdba21f5d66a8b26353d57a226f8e6a5fffc450c8836e90ab0e20d5e7935d0dea939d9a9b6a08781b9a7413184c - languageName: node - linkType: hard - -"jest-runner@npm:30.4.2": - version: 30.4.2 - resolution: "jest-runner@npm:30.4.2" - dependencies: - "@jest/console": "npm:30.4.1" - "@jest/environment": "npm:30.4.1" - "@jest/test-result": "npm:30.4.1" - "@jest/transform": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - chalk: "npm:^4.1.2" - emittery: "npm:^0.13.1" - exit-x: "npm:^0.2.2" - graceful-fs: "npm:^4.2.11" - jest-docblock: "npm:30.4.0" - jest-environment-node: "npm:30.4.1" - jest-haste-map: "npm:30.4.1" - jest-leak-detector: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-resolve: "npm:30.4.1" - jest-runtime: "npm:30.4.2" - jest-util: "npm:30.4.1" - jest-watcher: "npm:30.4.1" - jest-worker: "npm:30.4.1" - p-limit: "npm:^3.1.0" - source-map-support: "npm:0.5.13" - checksum: 10c0/339e630fb1a7db52e208ed9f12f722122733fe9a450d9bd83c0fccc10fbc5142a8808f624c41ab1e25833af02f9c3eca85561554b75a5b3ad75b4a226f72c5cf - languageName: node - linkType: hard - -"jest-runtime@npm:30.4.2": - version: 30.4.2 - resolution: "jest-runtime@npm:30.4.2" - dependencies: - "@jest/environment": "npm:30.4.1" - "@jest/fake-timers": "npm:30.4.1" - "@jest/globals": "npm:30.4.1" - "@jest/source-map": "npm:30.0.1" - "@jest/test-result": "npm:30.4.1" - "@jest/transform": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - chalk: "npm:^4.1.2" - cjs-module-lexer: "npm:^2.1.0" - collect-v8-coverage: "npm:^1.0.2" - glob: "npm:^10.5.0" - graceful-fs: "npm:^4.2.11" - jest-haste-map: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-mock: "npm:30.4.1" - jest-regex-util: "npm:30.4.0" - jest-resolve: "npm:30.4.1" - jest-snapshot: "npm:30.4.1" - jest-util: "npm:30.4.1" - slash: "npm:^3.0.0" - strip-bom: "npm:^4.0.0" - checksum: 10c0/9fce55b0c78fbe47dc2c10a944e9513833fd43c14f292460ef5cdd91e375088bf35549336e66f69fc9d29bf4f410894e9a7eef0bf12a6f39d99174a5300c2c53 - languageName: node - linkType: hard - -"jest-snapshot@npm:30.4.1": - version: 30.4.1 - resolution: "jest-snapshot@npm:30.4.1" - dependencies: - "@babel/core": "npm:^7.27.4" - "@babel/generator": "npm:^7.27.5" - "@babel/plugin-syntax-jsx": "npm:^7.27.1" - "@babel/plugin-syntax-typescript": "npm:^7.27.1" - "@babel/types": "npm:^7.27.3" - "@jest/expect-utils": "npm:30.4.1" - "@jest/get-type": "npm:30.1.0" - "@jest/snapshot-utils": "npm:30.4.1" - "@jest/transform": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - babel-preset-current-node-syntax: "npm:^1.2.0" - chalk: "npm:^4.1.2" - expect: "npm:30.4.1" - graceful-fs: "npm:^4.2.11" - jest-diff: "npm:30.4.1" - jest-matcher-utils: "npm:30.4.1" - jest-message-util: "npm:30.4.1" - jest-util: "npm:30.4.1" - pretty-format: "npm:30.4.1" - semver: "npm:^7.7.2" - synckit: "npm:^0.11.8" - checksum: 10c0/cebd70277b6f0d2606f22815480146cf1e37295ed69a1d16e260a99a2ab48db167857e2fb9a938923d22ac13203c83a5e31d7f066b58d87c6d42db58c914ff13 - languageName: node - linkType: hard - -"jest-util@npm:30.4.1": - version: 30.4.1 - resolution: "jest-util@npm:30.4.1" - dependencies: - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - chalk: "npm:^4.1.2" - ci-info: "npm:^4.2.0" - graceful-fs: "npm:^4.2.11" - picomatch: "npm:^4.0.3" - checksum: 10c0/3efe1f25e5a172d04c6af8612d82867ab603b7c1bd8cb89073ff834679b44eba178793cf3af162cf5e25be13aa736ebd23a7826683acc85bddc5873f305b1f6e - languageName: node - linkType: hard - -"jest-validate@npm:30.4.1": - version: 30.4.1 - resolution: "jest-validate@npm:30.4.1" - dependencies: - "@jest/get-type": "npm:30.1.0" - "@jest/types": "npm:30.4.1" - camelcase: "npm:^6.3.0" - chalk: "npm:^4.1.2" - leven: "npm:^3.1.0" - pretty-format: "npm:30.4.1" - checksum: 10c0/23e6677ee6d06476f368c8b6d442b4207e5fbe062e74c1da3eae9ed30a18605f4e8a14809fa9cc7f22a2d8446e8de91a512f59c278720db2ad61c77dc25ffefc - languageName: node - linkType: hard - -"jest-watcher@npm:30.4.1": - version: 30.4.1 - resolution: "jest-watcher@npm:30.4.1" - dependencies: - "@jest/test-result": "npm:30.4.1" - "@jest/types": "npm:30.4.1" - "@types/node": "npm:*" - ansi-escapes: "npm:^4.3.2" - chalk: "npm:^4.1.2" - emittery: "npm:^0.13.1" - jest-util: "npm:30.4.1" - string-length: "npm:^4.0.2" - checksum: 10c0/a56e1714b7b0f9c620c5cee95a84a48b780093594cd188e365a24768f208714895a0deb784ee48e4eec7f1828bc00435ab3c39208d490c33be3786937e997c97 - languageName: node - linkType: hard - -"jest-worker@npm:30.4.1": - version: 30.4.1 - resolution: "jest-worker@npm:30.4.1" - dependencies: - "@types/node": "npm:*" - "@ungap/structured-clone": "npm:^1.3.0" - jest-util: "npm:30.4.1" - merge-stream: "npm:^2.0.0" - supports-color: "npm:^8.1.1" - checksum: 10c0/3eb7ec7e928b82491e66ae6709e3a1eef3edad2bc351514a5d52037b997151989de6ce2912d6a5a3806ae3ae3bf6a1c36b1ad7bbc567d0790503fdb74576f140 - languageName: node - linkType: hard - -"jest@npm:^30.4.2": - version: 30.4.2 - resolution: "jest@npm:30.4.2" - dependencies: - "@jest/core": "npm:30.4.2" - "@jest/types": "npm:30.4.1" - import-local: "npm:^3.2.0" - jest-cli: "npm:30.4.2" - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - bin: - jest: ./bin/jest.js - checksum: 10c0/26a76eaabfc043abd8ee702b97f61ff968dde03412efdb4a69c22c99a5e4bf47788a3e45f75134aec1377a686a9d59d1e3bae85a816e409013475a80de1458ec - languageName: node - linkType: hard - "jju@npm:^1.4.0": version: 1.4.0 resolution: "jju@npm:1.4.0" @@ -5127,18 +3437,6 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:^3.13.1": - version: 3.15.2 - resolution: "js-yaml@npm:3.15.2" - dependencies: - argparse: "npm:^1.0.7" - esprima: "npm:^4.0.0" - bin: - js-yaml: bin/js-yaml.js - checksum: 10c0/e341df211dece9d4b784849647ad7568b5b6a58a9ee58ead750482316d83276387343649fe4b71522705dc6c76acf97718588f23dc19443833ef3e75033af967 - languageName: node - linkType: hard - "jsesc@npm:^3.0.2": version: 3.1.0 resolution: "jsesc@npm:3.1.0" @@ -5155,13 +3453,6 @@ __metadata: languageName: node linkType: hard -"json-parse-even-better-errors@npm:^2.3.0": - version: 2.3.1 - resolution: "json-parse-even-better-errors@npm:2.3.1" - checksum: 10c0/140932564c8f0b88455432e0f33c4cb4086b8868e37524e07e723f4eaedb9425bdc2bafd71bd1d9765bd15fd1e2d126972bc83990f55c467168c228c24d665f3 - languageName: node - linkType: hard - "json-schema-traverse@npm:^0.4.1": version: 0.4.1 resolution: "json-schema-traverse@npm:0.4.1" @@ -5226,13 +3517,6 @@ __metadata: languageName: node linkType: hard -"leven@npm:^3.1.0": - version: 3.1.0 - resolution: "leven@npm:3.1.0" - checksum: 10c0/cd778ba3fbab0f4d0500b7e87d1f6e1f041507c56fdcd47e8256a3012c98aaee371d4c15e0a76e0386107af2d42e2b7466160a2d80688aaa03e66e49949f42df - languageName: node - linkType: hard - "levn@npm:^0.4.1": version: 0.4.1 resolution: "levn@npm:0.4.1" @@ -5363,22 +3647,6 @@ __metadata: languageName: node linkType: hard -"lines-and-columns@npm:^1.1.6": - version: 1.2.4 - resolution: "lines-and-columns@npm:1.2.4" - checksum: 10c0/3da6ee62d4cd9f03f5dc90b4df2540fb85b352081bee77fe4bbcd12c9000ead7f35e0a38b8d09a9bb99b13223446dd8689ff3c4959807620726d788701a83d2d - languageName: node - linkType: hard - -"locate-path@npm:^5.0.0": - version: 5.0.0 - resolution: "locate-path@npm:5.0.0" - dependencies: - p-locate: "npm:^4.1.0" - checksum: 10c0/33a1c5247e87e022f9713e6213a744557a3e9ec32c5d0b5efb10aa3a38177615bf90221a5592674857039c1a0fd2063b82f285702d37b792d973e9e72ace6c59 - languageName: node - linkType: hard - "locate-path@npm:^6.0.0": version: 6.0.0 resolution: "locate-path@npm:6.0.0" @@ -5388,13 +3656,6 @@ __metadata: languageName: node linkType: hard -"lodash.memoize@npm:^4.1.2": - version: 4.1.2 - resolution: "lodash.memoize@npm:4.1.2" - checksum: 10c0/c8713e51eccc650422716a14cece1809cfe34bc5ab5e242b7f8b4e2241c2483697b971a604252807689b9dd69bfe3a98852e19a5b89d506b000b4187a1285df8 - languageName: node - linkType: hard - "loose-envify@npm:^1.0.0, loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" @@ -5415,13 +3676,6 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^10.2.0": - version: 10.4.3 - resolution: "lru-cache@npm:10.4.3" - checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb - languageName: node - linkType: hard - "lru-cache@npm:^5.1.1": version: 5.1.1 resolution: "lru-cache@npm:5.1.1" @@ -5480,22 +3734,6 @@ __metadata: languageName: node linkType: hard -"make-error@npm:^1.3.6": - version: 1.3.6 - resolution: "make-error@npm:1.3.6" - checksum: 10c0/171e458d86854c6b3fc46610cfacf0b45149ba043782558c6875d9f42f222124384ad0b468c92e996d815a8a2003817a710c0a160e49c1c394626f76fa45396f - languageName: node - linkType: hard - -"makeerror@npm:1.0.12": - version: 1.0.12 - resolution: "makeerror@npm:1.0.12" - dependencies: - tmpl: "npm:1.0.5" - checksum: 10c0/b0e6e599780ce6bab49cc413eba822f7d1f0dfebd1c103eaa3785c59e43e22c59018323cf9e1708f0ef5329e94a745d163fcbb6bff8e4c6742f9be9e86f3500c - languageName: node - linkType: hard - "mathjs@npm:^14.9.1": version: 14.9.1 resolution: "mathjs@npm:14.9.1" @@ -5515,20 +3753,6 @@ __metadata: languageName: node linkType: hard -"merge-stream@npm:^2.0.0": - version: 2.0.0 - resolution: "merge-stream@npm:2.0.0" - checksum: 10c0/867fdbb30a6d58b011449b8885601ec1690c3e41c759ecd5a9d609094f7aed0096c37823ff4a7190ef0b8f22cc86beb7049196ff68c016e3b3c671d0dac91ce5 - languageName: node - linkType: hard - -"mimic-fn@npm:^2.1.0": - version: 2.1.0 - resolution: "mimic-fn@npm:2.1.0" - checksum: 10c0/b26f5479d7ec6cc2bce275a08f146cf78f5e7b661b18114e2506dd91ec7ec47e7a25bf4360e5438094db0560bcc868079fb3b1fb3892b833c1ecbf63f80c95a4 - languageName: node - linkType: hard - "minimatch@npm:^10.2.2, minimatch@npm:^10.2.4, minimatch@npm:^10.2.5": version: 10.2.5 resolution: "minimatch@npm:10.2.5" @@ -5538,15 +3762,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1": - version: 3.1.5 - resolution: "minimatch@npm:3.1.5" - dependencies: - brace-expansion: "npm:^1.1.7" - checksum: 10c0/2ecbdc0d33f07bddb0315a8b5afbcb761307a8778b48f0b312418ccbced99f104a2d17d8aca7573433c70e8ccd1c56823a441897a45e384ea76ef401a26ace70 - languageName: node - linkType: hard - "minimatch@npm:^9.0.3 || ^10.1.2": version: 10.2.6 resolution: "minimatch@npm:10.2.6" @@ -5556,23 +3771,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.4": - version: 9.0.9 - resolution: "minimatch@npm:9.0.9" - dependencies: - brace-expansion: "npm:^2.0.2" - checksum: 10c0/0b6a58530dbb00361745aa6c8cffaba4c90f551afe7c734830bd95fd88ebf469dd7355a027824ea1d09e37181cfeb0a797fb17df60c15ac174303ac110eb7e86 - languageName: node - linkType: hard - -"minimist@npm:^1.2.8": - version: 1.2.8 - resolution: "minimist@npm:1.2.8" - checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6 - languageName: node - linkType: hard - -"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.4, minipass@npm:^7.1.2": +"minipass@npm:^7.0.4, minipass@npm:^7.1.2": version: 7.1.3 resolution: "minipass@npm:7.1.3" checksum: 10c0/539da88daca16533211ea5a9ee98dc62ff5742f531f54640dd34429e621955e91cc280a91a776026264b7f9f6735947629f920944e9c1558369e8bf22eb33fbb @@ -5644,13 +3843,6 @@ __metadata: languageName: node linkType: hard -"neo-async@npm:^2.6.2": - version: 2.6.2 - resolution: "neo-async@npm:2.6.2" - checksum: 10c0/c2f5a604a54a8ec5438a342e1f356dff4bc33ccccdb6dc668d94fe8e5eccfc9d2c2eea6064b0967a767ba63b33763f51ccf2cd2441b461a7322656c1f06b3f5d - languageName: node - linkType: hard - "no-case@npm:^3.0.4": version: 3.0.4 resolution: "no-case@npm:3.0.4" @@ -5681,13 +3873,6 @@ __metadata: languageName: node linkType: hard -"node-int64@npm:^0.4.0": - version: 0.4.0 - resolution: "node-int64@npm:0.4.0" - checksum: 10c0/a6a4d8369e2f2720e9c645255ffde909c0fbd41c92ea92a5607fc17055955daac99c1ff589d421eee12a0d24e99f7bfc2aabfeb1a4c14742f6c099a51863f31a - languageName: node - linkType: hard - "node-releases@npm:^2.0.36": version: 2.0.38 resolution: "node-releases@npm:2.0.38" @@ -5706,13 +3891,6 @@ __metadata: languageName: node linkType: hard -"normalize-path@npm:^3.0.0": - version: 3.0.0 - resolution: "normalize-path@npm:3.0.0" - checksum: 10c0/e008c8142bcc335b5e38cf0d63cfd39d6cf2d97480af9abdbe9a439221fd4d749763bab492a8ee708ce7a194bb00c9da6d0a115018672310850489137b3da046 - languageName: node - linkType: hard - "normalize.css@npm:^8.0.1": version: 8.0.1 resolution: "normalize.css@npm:8.0.1" @@ -5720,15 +3898,6 @@ __metadata: languageName: node linkType: hard -"npm-run-path@npm:^4.0.1": - version: 4.0.1 - resolution: "npm-run-path@npm:4.0.1" - dependencies: - path-key: "npm:^3.0.0" - checksum: 10c0/6f9353a95288f8455cf64cbeb707b28826a7f29690244c1e4bb61ec573256e021b6ad6651b394eb1ccfd00d6ec50147253aba2c5fe58a57ceb111fad62c519ac - languageName: node - linkType: hard - "object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" @@ -5757,24 +3926,6 @@ __metadata: languageName: node linkType: hard -"once@npm:^1.3.0": - version: 1.4.0 - resolution: "once@npm:1.4.0" - dependencies: - wrappy: "npm:1" - checksum: 10c0/5d48aca287dfefabd756621c5dfce5c91a549a93e9fdb7b8246bc4c4790aa2ec17b34a260530474635147aeb631a2dcc8b32c613df0675f96041cbb8244517d0 - languageName: node - linkType: hard - -"onetime@npm:^5.1.2": - version: 5.1.2 - resolution: "onetime@npm:5.1.2" - dependencies: - mimic-fn: "npm:^2.1.0" - checksum: 10c0/ffcef6fbb2692c3c40749f31ea2e22677a876daea92959b8a80b521d95cca7a668c884d8b2045d1d8ee7d56796aa405c405462af112a1477594cc63531baeb8f - languageName: node - linkType: hard - "optionator@npm:^0.9.3": version: 0.9.4 resolution: "optionator@npm:0.9.4" @@ -5789,16 +3940,7 @@ __metadata: languageName: node linkType: hard -"p-limit@npm:^2.2.0": - version: 2.3.0 - resolution: "p-limit@npm:2.3.0" - dependencies: - p-try: "npm:^2.0.0" - checksum: 10c0/8da01ac53efe6a627080fafc127c873da40c18d87b3f5d5492d465bb85ec7207e153948df6b9cbaeb130be70152f874229b8242ee2be84c0794082510af97f12 - languageName: node - linkType: hard - -"p-limit@npm:^3.0.2, p-limit@npm:^3.1.0": +"p-limit@npm:^3.0.2": version: 3.1.0 resolution: "p-limit@npm:3.1.0" dependencies: @@ -5807,15 +3949,6 @@ __metadata: languageName: node linkType: hard -"p-locate@npm:^4.1.0": - version: 4.1.0 - resolution: "p-locate@npm:4.1.0" - dependencies: - p-limit: "npm:^2.2.0" - checksum: 10c0/1b476ad69ad7f6059744f343b26d51ce091508935c1dbb80c4e0a2f397ffce0ca3a1f9f5cd3c7ce19d7929a09719d5c65fe70d8ee289c3f267cd36f2881813e9 - languageName: node - linkType: hard - "p-locate@npm:^5.0.0": version: 5.0.0 resolution: "p-locate@npm:5.0.0" @@ -5825,20 +3958,6 @@ __metadata: languageName: node linkType: hard -"p-try@npm:^2.0.0": - version: 2.2.0 - resolution: "p-try@npm:2.2.0" - checksum: 10c0/c36c19907734c904b16994e6535b02c36c2224d433e01a2f1ab777237f4d86e6289fd5fd464850491e940379d4606ed850c03e0f9ab600b0ebddb511312e177f - languageName: node - linkType: hard - -"package-json-from-dist@npm:^1.0.0": - version: 1.0.1 - resolution: "package-json-from-dist@npm:1.0.1" - checksum: 10c0/62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b - languageName: node - linkType: hard - "package-manager-detector@npm:^1.6.0, package-manager-detector@npm:^1.8.0": version: 1.9.0 resolution: "package-manager-detector@npm:1.9.0" @@ -5856,18 +3975,6 @@ __metadata: languageName: node linkType: hard -"parse-json@npm:^5.2.0": - version: 5.2.0 - resolution: "parse-json@npm:5.2.0" - dependencies: - "@babel/code-frame": "npm:^7.0.0" - error-ex: "npm:^1.3.1" - json-parse-even-better-errors: "npm:^2.3.0" - lines-and-columns: "npm:^1.1.6" - checksum: 10c0/77947f2253005be7a12d858aedbafa09c9ae39eb4863adf330f7b416ca4f4a08132e453e08de2db46459256fb66afaac5ee758b44fe6541b7cdaf9d252e59585 - languageName: node - linkType: hard - "pascal-case@npm:^3.1.2": version: 3.1.2 resolution: "pascal-case@npm:3.1.2" @@ -5895,14 +4002,7 @@ __metadata: languageName: node linkType: hard -"path-is-absolute@npm:^1.0.0": - version: 1.0.1 - resolution: "path-is-absolute@npm:1.0.1" - checksum: 10c0/127da03c82172a2a50099cddbf02510c1791fc2cc5f7713ddb613a56838db1e8168b121a920079d052e0936c23005562059756d653b7c544c53185efe53be078 - languageName: node - linkType: hard - -"path-key@npm:^3.0.0, path-key@npm:^3.1.0": +"path-key@npm:^3.1.0": version: 3.1.1 resolution: "path-key@npm:3.1.1" checksum: 10c0/748c43efd5a569c039d7a00a03b58eecd1d75f3999f5a28303d75f521288df4823bc057d8784eb72358b2895a05f29a070bc9f1f17d28226cc4e62494cc58c4c @@ -5916,16 +4016,6 @@ __metadata: languageName: node linkType: hard -"path-scurry@npm:^1.11.1": - version: 1.11.1 - resolution: "path-scurry@npm:1.11.1" - dependencies: - lru-cache: "npm:^10.2.0" - minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" - checksum: 10c0/32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d - languageName: node - linkType: hard - "picocolors@npm:^1.1.1": version: 1.1.1 resolution: "picocolors@npm:1.1.1" @@ -5933,14 +4023,7 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^2.0.4": - version: 2.3.2 - resolution: "picomatch@npm:2.3.2" - checksum: 10c0/a554d1709e59be97d1acb9eaedbbc700a5c03dbd4579807baed95100b00420bc729335440ef15004ae2378984e2487a7c1cebd743cfdb72b6fa9ab69223c0d61 - languageName: node - linkType: hard - -"picomatch@npm:^4.0.2, picomatch@npm:^4.0.3, picomatch@npm:^4.0.4": +"picomatch@npm:^4.0.2, picomatch@npm:^4.0.4": version: 4.0.4 resolution: "picomatch@npm:4.0.4" checksum: 10c0/e2c6023372cc7b5764719a5ffb9da0f8e781212fa7ca4bd0562db929df8e117460f00dff3cb7509dacfc06b86de924b247f504d0ce1806a37fac4633081466b0 @@ -5954,22 +4037,6 @@ __metadata: languageName: node linkType: hard -"pirates@npm:^4.0.7": - version: 4.0.7 - resolution: "pirates@npm:4.0.7" - checksum: 10c0/a51f108dd811beb779d58a76864bbd49e239fa40c7984cd11596c75a121a8cc789f1c8971d8bb15f0dbf9d48b76c05bb62fcbce840f89b688c0fa64b37e8478a - languageName: node - linkType: hard - -"pkg-dir@npm:^4.2.0": - version: 4.2.0 - resolution: "pkg-dir@npm:4.2.0" - dependencies: - find-up: "npm:^4.0.0" - checksum: 10c0/c56bda7769e04907a88423feb320babaed0711af8c436ce3e56763ab1021ba107c7b0cafb11cde7529f669cfc22bffcaebffb573645cbd63842ea9fb17cd7728 - languageName: node - linkType: hard - "postcss@npm:^8.5.29": version: 8.5.29 resolution: "postcss@npm:8.5.29" @@ -5997,18 +4064,6 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:30.4.1, pretty-format@npm:^30.0.0": - version: 30.4.1 - resolution: "pretty-format@npm:30.4.1" - dependencies: - "@jest/schemas": "npm:30.4.1" - ansi-styles: "npm:^5.2.0" - react-is-18: "npm:react-is@^18.3.1" - react-is-19: "npm:react-is@^19.2.5" - checksum: 10c0/c7e6633740cd2f6d382f188c00c8b4b3f2bee3cda16db6753471c6bb4b94f76531358d3a7793062a0fb00d72ebfb934e8ae1d4f5ced6bb34c8e7f60996f90076 - languageName: node - linkType: hard - "proc-log@npm:^6.0.0": version: 6.1.0 resolution: "proc-log@npm:6.1.0" @@ -6034,13 +4089,6 @@ __metadata: languageName: node linkType: hard -"pure-rand@npm:^7.0.0": - version: 7.0.1 - resolution: "pure-rand@npm:7.0.1" - checksum: 10c0/9cade41030f5ec95f5d55a11a71404cd6f46b69becaad892097cd7f58e2c6248cd0a933349ca7d21336ab629f1da42ffe899699b671bc4651600eaf6e57f837e - languageName: node - linkType: hard - "py-slang@github:proto-aiken-13/py-slang#branch-ev3-engine": version: 1.0.0 resolution: "py-slang@https://github.com/proto-aiken-13/py-slang.git#commit=da6c3870cd7c4319d250484c8edf15ef4de21555" @@ -6091,20 +4139,6 @@ __metadata: languageName: node linkType: hard -"react-is-18@npm:react-is@^18.3.1": - version: 18.3.1 - resolution: "react-is@npm:18.3.1" - checksum: 10c0/f2f1e60010c683479e74c63f96b09fb41603527cd131a9959e2aee1e5a8b0caf270b365e5ca77d4a6b18aae659b60a86150bb3979073528877029b35aecd2072 - languageName: node - linkType: hard - -"react-is-19@npm:react-is@^19.2.5": - version: 19.2.7 - resolution: "react-is@npm:19.2.7" - checksum: 10c0/419fe54d5bd7fdf5414a5bb7bd9a1e0e36f9fae28ffb4cb73290fbe342bde15d8584a90d1db62547f6aa03018dce517b178a041abb522136cd4b4b51b4e94c83 - languageName: node - linkType: hard - "react-is@npm:^16.13.1": version: 16.13.1 resolution: "react-is@npm:16.13.1" @@ -6194,29 +4228,6 @@ __metadata: languageName: node linkType: hard -"require-directory@npm:^2.1.1": - version: 2.1.1 - resolution: "require-directory@npm:2.1.1" - checksum: 10c0/83aa76a7bc1531f68d92c75a2ca2f54f1b01463cb566cf3fbc787d0de8be30c9dbc211d1d46be3497dac5785fe296f2dd11d531945ac29730643357978966e99 - languageName: node - linkType: hard - -"resolve-cwd@npm:^3.0.0": - version: 3.0.0 - resolution: "resolve-cwd@npm:3.0.0" - dependencies: - resolve-from: "npm:^5.0.0" - checksum: 10c0/e608a3ebd15356264653c32d7ecbc8fd702f94c6703ea4ac2fb81d9c359180cba0ae2e6b71faa446631ed6145454d5a56b227efc33a2d40638ac13f8beb20ee4 - languageName: node - linkType: hard - -"resolve-from@npm:^5.0.0": - version: 5.0.0 - resolution: "resolve-from@npm:5.0.0" - checksum: 10c0/b21cb7f1fb746de8107b9febab60095187781137fd803e6a59a76d421444b1531b641bba5857f5dc011974d8a5c635d61cec49e6bd3b7fc20e01f0fafc4efbf2 - languageName: node - linkType: hard - "resolve-pkg-maps@npm:^1.0.0": version: 1.0.0 resolution: "resolve-pkg-maps@npm:1.0.0" @@ -6430,7 +4441,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.7.2, semver@npm:^7.7.3, semver@npm:^7.8.0, semver@npm:^7.8.1": +"semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.7.2, semver@npm:^7.7.3, semver@npm:^7.8.1": version: 7.8.5 resolution: "semver@npm:7.8.5" bin: @@ -6480,20 +4491,6 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.3": - version: 3.0.7 - resolution: "signal-exit@npm:3.0.7" - checksum: 10c0/25d272fa73e146048565e08f3309d5b942c1979a6f4a58a8c59d5fa299728e9c2fcd1a759ec870863b1fd38653670240cd420dad2ad9330c71f36608a6a1c912 - languageName: node - linkType: hard - -"signal-exit@npm:^4.0.1": - version: 4.1.0 - resolution: "signal-exit@npm:4.1.0" - checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 - languageName: node - linkType: hard - "sisteransi@npm:^1.0.5": version: 1.0.5 resolution: "sisteransi@npm:1.0.5" @@ -6501,13 +4498,6 @@ __metadata: languageName: node linkType: hard -"slash@npm:^3.0.0": - version: 3.0.0 - resolution: "slash@npm:3.0.0" - checksum: 10c0/e18488c6a42bdfd4ac5be85b2ced3ccd0224773baae6ad42cfbb9ec74fc07f9fa8396bd35ee638084ead7a2a0818eb5e7151111544d4731ce843019dab4be47b - languageName: node - linkType: hard - "smob@npm:^1.0.0": version: 1.6.1 resolution: "smob@npm:1.6.1" @@ -6532,16 +4522,6 @@ __metadata: languageName: node linkType: hard -"source-map-support@npm:0.5.13": - version: 0.5.13 - resolution: "source-map-support@npm:0.5.13" - dependencies: - buffer-from: "npm:^1.0.0" - source-map: "npm:^0.6.0" - checksum: 10c0/137539f8c453fa0f496ea42049ab5da4569f96781f6ac8e5bfda26937be9494f4e8891f523c5f98f0e85f71b35d74127a00c46f83f6a4f54672b58d53202565e - languageName: node - linkType: hard - "source-map-support@npm:~0.5.20": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" @@ -6552,20 +4532,13 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.6.0, source-map@npm:^0.6.1": +"source-map@npm:^0.6.0": version: 0.6.1 resolution: "source-map@npm:0.6.1" checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 languageName: node linkType: hard -"sprintf-js@npm:~1.0.2": - version: 1.0.3 - resolution: "sprintf-js@npm:1.0.3" - checksum: 10c0/ecadcfe4c771890140da5023d43e190b7566d9cf8b2d238600f31bec0fc653f328da4450eb04bd59a431771a8e9cc0e118f0aa3974b683a4981b4e07abc2a5bb - languageName: node - linkType: hard - "stable-hash-x@npm:^0.2.0": version: 0.2.0 resolution: "stable-hash-x@npm:0.2.0" @@ -6573,15 +4546,6 @@ __metadata: languageName: node linkType: hard -"stack-utils@npm:^2.0.6": - version: 2.0.6 - resolution: "stack-utils@npm:2.0.6" - dependencies: - escape-string-regexp: "npm:^2.0.0" - checksum: 10c0/651c9f87667e077584bbe848acaecc6049bc71979f1e9a46c7b920cad4431c388df0f51b8ad7cfd6eed3db97a2878d0fc8b3122979439ea8bac29c61c95eec8a - languageName: node - linkType: hard - "std-env@npm:^4.2.0": version: 4.3.0 resolution: "std-env@npm:4.3.0" @@ -6589,77 +4553,6 @@ __metadata: languageName: node linkType: hard -"string-length@npm:^4.0.2": - version: 4.0.2 - resolution: "string-length@npm:4.0.2" - dependencies: - char-regex: "npm:^1.0.2" - strip-ansi: "npm:^6.0.0" - checksum: 10c0/1cd77409c3d7db7bc59406f6bcc9ef0783671dcbabb23597a1177c166906ef2ee7c8290f78cae73a8aec858768f189d2cb417797df5e15ec4eb5e16b3346340c - languageName: node - linkType: hard - -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": - version: 4.2.3 - resolution: "string-width@npm:4.2.3" - dependencies: - emoji-regex: "npm:^8.0.0" - is-fullwidth-code-point: "npm:^3.0.0" - strip-ansi: "npm:^6.0.1" - checksum: 10c0/1e525e92e5eae0afd7454086eed9c818ee84374bb80328fc41217ae72ff5f065ef1c9d7f72da41de40c75fa8bb3dee63d92373fd492c84260a552c636392a47b - languageName: node - linkType: hard - -"string-width@npm:^5.0.1, string-width@npm:^5.1.2": - version: 5.1.2 - resolution: "string-width@npm:5.1.2" - dependencies: - eastasianwidth: "npm:^0.2.0" - emoji-regex: "npm:^9.2.2" - strip-ansi: "npm:^7.0.1" - checksum: 10c0/ab9c4264443d35b8b923cbdd513a089a60de339216d3b0ed3be3ba57d6880e1a192b70ae17225f764d7adbf5994e9bb8df253a944736c15a0240eff553c678ca - languageName: node - linkType: hard - -"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": - version: 6.0.1 - resolution: "strip-ansi@npm:6.0.1" - dependencies: - ansi-regex: "npm:^5.0.1" - checksum: 10c0/1ae5f212a126fe5b167707f716942490e3933085a5ff6c008ab97ab2f272c8025d3aa218b7bd6ab25729ca20cc81cddb252102f8751e13482a5199e873680952 - languageName: node - linkType: hard - -"strip-ansi@npm:^7.0.1": - version: 7.2.0 - resolution: "strip-ansi@npm:7.2.0" - dependencies: - ansi-regex: "npm:^6.2.2" - checksum: 10c0/544d13b7582f8254811ea97db202f519e189e59d35740c46095897e254e4f1aa9fe1524a83ad6bc5ad67d4dd6c0281d2e0219ed62b880a6238a16a17d375f221 - languageName: node - linkType: hard - -"strip-bom@npm:^4.0.0": - version: 4.0.0 - resolution: "strip-bom@npm:4.0.0" - checksum: 10c0/26abad1172d6bc48985ab9a5f96c21e440f6e7e476686de49be813b5a59b3566dccb5c525b831ec54fe348283b47f3ffb8e080bc3f965fde12e84df23f6bb7ef - languageName: node - linkType: hard - -"strip-final-newline@npm:^2.0.0": - version: 2.0.0 - resolution: "strip-final-newline@npm:2.0.0" - checksum: 10c0/bddf8ccd47acd85c0e09ad7375409d81653f645fda13227a9d459642277c253d877b68f2e5e4d819fe75733b0e626bac7e954c04f3236f6d196f79c94fa4a96f - languageName: node - linkType: hard - -"strip-json-comments@npm:^3.1.1": - version: 3.1.1 - resolution: "strip-json-comments@npm:3.1.1" - checksum: 10c0/9681a6257b925a7fa0f285851c0e613cc934a50661fa7bb41ca9cbbff89686bb4a0ee366e6ecedc4daafd01e83eee0720111ab294366fe7c185e935475ebcecd - languageName: node - linkType: hard - "supports-color@npm:^7.1.0": version: 7.2.0 resolution: "supports-color@npm:7.2.0" @@ -6669,15 +4562,6 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^8.1.1": - version: 8.1.1 - resolution: "supports-color@npm:8.1.1" - dependencies: - has-flag: "npm:^4.0.0" - checksum: 10c0/ea1d3c275dd604c974670f63943ed9bd83623edc102430c05adb8efc56ba492746b6e95386e7831b872ec3807fd89dd8eb43f735195f37b5ec343e4234cc7e89 - languageName: node - linkType: hard - "supports-preserve-symlinks-flag@npm:^1.0.0": version: 1.0.0 resolution: "supports-preserve-symlinks-flag@npm:1.0.0" @@ -6685,15 +4569,6 @@ __metadata: languageName: node linkType: hard -"synckit@npm:^0.11.8": - version: 0.11.13 - resolution: "synckit@npm:0.11.13" - dependencies: - "@pkgr/core": "npm:^0.3.6" - checksum: 10c0/5a6c19f4f79045aaa7994106401bff6dbe7cca23a6d0a0723ff14eb8b1bebeb4a71729118f6914905598e304ea2fa13509885e11ba07d92e7cb68a06740cb328 - languageName: node - linkType: hard - "tabbable@npm:^6.0.0": version: 6.5.0 resolution: "tabbable@npm:6.5.0" @@ -6728,17 +4603,6 @@ __metadata: languageName: node linkType: hard -"test-exclude@npm:^6.0.0": - version: 6.0.0 - resolution: "test-exclude@npm:6.0.0" - dependencies: - "@istanbuljs/schema": "npm:^0.1.2" - glob: "npm:^7.1.4" - minimatch: "npm:^3.0.4" - checksum: 10c0/019d33d81adff3f9f1bfcff18125fb2d3c65564f437d9be539270ee74b994986abb8260c7c2ce90e8f30162178b09dbbce33c6389273afac4f36069c48521f57 - languageName: node - linkType: hard - "tiny-emitter@npm:^2.1.0": version: 2.1.0 resolution: "tiny-emitter@npm:2.1.0" @@ -6784,13 +4648,6 @@ __metadata: languageName: node linkType: hard -"tmpl@npm:1.0.5": - version: 1.0.5 - resolution: "tmpl@npm:1.0.5" - checksum: 10c0/f935537799c2d1922cb5d6d3805f594388f75338fe7a4a9dac41504dd539704ca4db45b883b52e7b0aa5b2fd5ddadb1452bf95cd23a69da2f793a843f9451cc9 - languageName: node - linkType: hard - "ts-api-utils@npm:^2.5.0": version: 2.5.0 resolution: "ts-api-utils@npm:2.5.0" @@ -6800,46 +4657,6 @@ __metadata: languageName: node linkType: hard -"ts-jest@npm:^29.4.11": - version: 29.4.11 - resolution: "ts-jest@npm:29.4.11" - dependencies: - bs-logger: "npm:^0.2.6" - fast-json-stable-stringify: "npm:^2.1.0" - handlebars: "npm:^4.7.9" - json5: "npm:^2.2.3" - lodash.memoize: "npm:^4.1.2" - make-error: "npm:^1.3.6" - semver: "npm:^7.8.0" - type-fest: "npm:^4.41.0" - yargs-parser: "npm:^21.1.1" - peerDependencies: - "@babel/core": ">=7.0.0-beta.0 <8" - "@jest/transform": ^29.0.0 || ^30.0.0 - "@jest/types": ^29.0.0 || ^30.0.0 - babel-jest: ^29.0.0 || ^30.0.0 - jest: ^29.0.0 || ^30.0.0 - jest-util: ^29.0.0 || ^30.0.0 - typescript: ">=4.3 <7" - peerDependenciesMeta: - "@babel/core": - optional: true - "@jest/transform": - optional: true - "@jest/types": - optional: true - babel-jest: - optional: true - esbuild: - optional: true - jest-util: - optional: true - bin: - ts-jest: cli.js - checksum: 10c0/f9e6ab3235f33088c4d6441da97d4b03b1fe9c21f4d859d7acf3f325ea36471a6c1ea9301f445b2670efa1a391dcddc31ecd8641a2d673752d074136311b8480 - languageName: node - linkType: hard - "tslib@npm:^2.0.3, tslib@npm:^2.4.0, tslib@npm:^2.8.1": version: 2.8.1 resolution: "tslib@npm:2.8.1" @@ -6863,27 +4680,6 @@ __metadata: languageName: node linkType: hard -"type-detect@npm:4.0.8": - version: 4.0.8 - resolution: "type-detect@npm:4.0.8" - checksum: 10c0/8fb9a51d3f365a7de84ab7f73b653534b61b622aa6800aecdb0f1095a4a646d3f5eb295322127b6573db7982afcd40ab492d038cf825a42093a58b1e1353e0bd - languageName: node - linkType: hard - -"type-fest@npm:^0.21.3": - version: 0.21.3 - resolution: "type-fest@npm:0.21.3" - checksum: 10c0/902bd57bfa30d51d4779b641c2bc403cdf1371fb9c91d3c058b0133694fcfdb817aef07a47f40faf79039eecbaa39ee9d3c532deff244f3a19ce68cea71a61e8 - languageName: node - linkType: hard - -"type-fest@npm:^4.41.0": - version: 4.41.0 - resolution: "type-fest@npm:4.41.0" - checksum: 10c0/f5ca697797ed5e88d33ac8f1fec21921839871f808dc59345c9cf67345bfb958ce41bd821165dbf3ae591cedec2bf6fe8882098dfdd8dc54320b859711a2c1e4 - languageName: node - linkType: hard - "typed-function@npm:^4.2.1": version: 4.2.2 resolution: "typed-function@npm:4.2.2" @@ -6926,15 +4722,6 @@ __metadata: languageName: node linkType: hard -"uglify-js@npm:^3.1.4": - version: 3.19.3 - resolution: "uglify-js@npm:3.19.3" - bin: - uglifyjs: bin/uglifyjs - checksum: 10c0/83b0a90eca35f778e07cad9622b80c448b6aad457c9ff8e568afed978212b42930a95f9e1be943a1ffa4258a3340fbb899f41461131c05bb1d0a9c303aed8479 - languageName: node - linkType: hard - "undici-types@npm:>=7.24.0 <7.24.7": version: 7.24.6 resolution: "undici-types@npm:7.24.6" @@ -6942,13 +4729,6 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~8.9.0": - version: 8.9.0 - resolution: "undici-types@npm:8.9.0" - checksum: 10c0/51c712c20c087368f62c6fc0f2bc49fa76bfe5dec28ff01584bb677ff29b0622569783c02d43e151143808c48c0503f804093bd2c491b10f2a830138034cb451 - languageName: node - linkType: hard - "undici@npm:^6.25.0": version: 6.27.0 resolution: "undici@npm:6.27.0" @@ -6956,7 +4736,7 @@ __metadata: languageName: node linkType: hard -"unrs-resolver@npm:^1.7.11, unrs-resolver@npm:^1.9.2": +"unrs-resolver@npm:^1.9.2": version: 1.12.2 resolution: "unrs-resolver@npm:1.12.2" dependencies: @@ -7073,17 +4853,6 @@ __metadata: languageName: node linkType: hard -"v8-to-istanbul@npm:^9.0.1": - version: 9.3.0 - resolution: "v8-to-istanbul@npm:9.3.0" - dependencies: - "@jridgewell/trace-mapping": "npm:^0.3.12" - "@types/istanbul-lib-coverage": "npm:^2.0.1" - convert-source-map: "npm:^2.0.0" - checksum: 10c0/968bcf1c7c88c04df1ffb463c179558a2ec17aa49e49376120504958239d9e9dad5281aa05f2a78542b8557f2be0b0b4c325710262f3b838b40d703d5ed30c23 - languageName: node - linkType: hard - "verkit@npm:^0.5.0": version: 0.5.0 resolution: "verkit@npm:0.5.0" @@ -7226,15 +4995,6 @@ __metadata: languageName: node linkType: hard -"walker@npm:^1.0.8": - version: 1.0.8 - resolution: "walker@npm:1.0.8" - dependencies: - makeerror: "npm:1.0.12" - checksum: 10c0/a17e037bccd3ca8a25a80cb850903facdfed0de4864bd8728f1782370715d679fa72e0a0f5da7c1c1379365159901e5935f35be531229da53bbfc0efdabdb48e - languageName: node - linkType: hard - "warning@npm:^4.0.2": version: 4.0.3 resolution: "warning@npm:4.0.3" @@ -7282,59 +5042,6 @@ __metadata: languageName: node linkType: hard -"wordwrap@npm:^1.0.0": - version: 1.0.0 - resolution: "wordwrap@npm:1.0.0" - checksum: 10c0/7ed2e44f3c33c5c3e3771134d2b0aee4314c9e49c749e37f464bf69f2bcdf0cbf9419ca638098e2717cff4875c47f56a007532f6111c3319f557a2ca91278e92 - languageName: node - linkType: hard - -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": - version: 7.0.0 - resolution: "wrap-ansi@npm:7.0.0" - dependencies: - ansi-styles: "npm:^4.0.0" - string-width: "npm:^4.1.0" - strip-ansi: "npm:^6.0.0" - checksum: 10c0/d15fc12c11e4cbc4044a552129ebc75ee3f57aa9c1958373a4db0292d72282f54373b536103987a4a7594db1ef6a4f10acf92978f79b98c49306a4b58c77d4da - languageName: node - linkType: hard - -"wrap-ansi@npm:^8.1.0": - version: 8.1.0 - resolution: "wrap-ansi@npm:8.1.0" - dependencies: - ansi-styles: "npm:^6.1.0" - string-width: "npm:^5.0.1" - strip-ansi: "npm:^7.0.1" - checksum: 10c0/138ff58a41d2f877eae87e3282c0630fc2789012fc1af4d6bd626eeb9a2f9a65ca92005e6e69a75c7b85a68479fe7443c7dbe1eb8fbaa681a4491364b7c55c60 - languageName: node - linkType: hard - -"wrappy@npm:1": - version: 1.0.2 - resolution: "wrappy@npm:1.0.2" - checksum: 10c0/56fece1a4018c6a6c8e28fbc88c87e0fbf4ea8fd64fc6c63b18f4acc4bd13e0ad2515189786dd2c30d3eec9663d70f4ecf699330002f8ccb547e4a18231fc9f0 - languageName: node - linkType: hard - -"write-file-atomic@npm:^5.0.1": - version: 5.0.1 - resolution: "write-file-atomic@npm:5.0.1" - dependencies: - imurmurhash: "npm:^0.1.4" - signal-exit: "npm:^4.0.1" - checksum: 10c0/e8c850a8e3e74eeadadb8ad23c9d9d63e4e792bd10f4836ed74189ef6e996763959f1249c5650e232f3c77c11169d239cbfc8342fc70f3fe401407d23810505d - languageName: node - linkType: hard - -"y18n@npm:^5.0.5": - version: 5.0.8 - resolution: "y18n@npm:5.0.8" - checksum: 10c0/4df2842c36e468590c3691c894bc9cdbac41f520566e76e24f59401ba7d8b4811eb1e34524d57e54bc6d864bcb66baab7ffd9ca42bf1eda596618f9162b91249 - languageName: node - linkType: hard - "yallist@npm:^3.0.2": version: 3.1.1 resolution: "yallist@npm:3.1.1" @@ -7358,28 +5065,6 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^21.1.1": - version: 21.1.1 - resolution: "yargs-parser@npm:21.1.1" - checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2 - languageName: node - linkType: hard - -"yargs@npm:^17.7.2": - version: 17.7.3 - resolution: "yargs@npm:17.7.3" - dependencies: - cliui: "npm:^8.0.1" - escalade: "npm:^3.1.1" - get-caller-file: "npm:^2.0.5" - require-directory: "npm:^2.1.1" - string-width: "npm:^4.2.3" - y18n: "npm:^5.0.5" - yargs-parser: "npm:^21.1.1" - checksum: 10c0/7a28572f7e785a57886e34fdbddb9b28756dec552e1453d5f6e7cdd00ad8721a4e8c4321d33683f5e61cacb36ad43258adbb48396b71ec4ed14abee0fc0d0c1f - languageName: node - linkType: hard - "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0" From 4239d42f730e3ffb9142da4931c62373cb239572 Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Sun, 18 Oct 2026 03:32:15 +0800 Subject: [PATCH 28/29] Run format --- src/common/autocomplete/src/syntax.ts | 4 +- src/common/cse-machine/src/index.ts | 2 +- src/common/data-visualizer/src/index.ts | 10 +- src/common/e-stepper/package.json | 4 +- src/common/e-stepper/rollup.config.mjs | 24 +- .../e-stepper/src/__tests__/common.test.ts | 68 ++-- src/common/e-stepper/src/index.ts | 34 +- src/runner/e-stepper/package.json | 4 +- src/runner/e-stepper/rollup.config.mjs | 24 +- .../e-stepper/src/__tests__/runner.test.ts | 36 +- src/runner/e-stepper/src/index.ts | 4 +- .../src/__tests__/classify.test.ts | 24 +- .../src/__tests__/format.test.ts | 18 +- .../src/__tests__/treeViews.test.ts | 66 ++-- src/web/data-visualizer/src/classify.ts | 14 +- src/web/data-visualizer/src/format.ts | 6 +- .../src/tree/BinaryTreeDrawer.tsx | 14 +- .../src/tree/ClassicDrawer.tsx | 10 +- .../src/tree/GeneralTreeDrawer.tsx | 14 +- src/web/data-visualizer/src/tree/Tree.ts | 4 +- src/web/e-stepper/package.json | 3 +- src/web/e-stepper/rollup.config.mjs | 36 +- src/web/e-stepper/src/EStepperHostPlugin.tsx | 26 +- src/web/e-stepper/src/EStepperView.tsx | 96 ++--- src/web/e-stepper/src/EnvDiagram.tsx | 38 +- src/web/e-stepper/src/ProgramArrows.tsx | 50 +-- .../src/__tests__/EStepperHostPlugin.test.tsx | 38 +- .../src/__tests__/EStepperView.test.tsx | 350 +++++++++--------- .../src/__tests__/EnvDiagram.test.tsx | 164 ++++---- .../src/__tests__/ProgramArrows.test.tsx | 124 +++---- .../e-stepper/src/__tests__/layout.test.ts | 102 ++--- .../e-stepper/src/__tests__/styles.test.ts | 34 +- src/web/e-stepper/src/colors.ts | 32 +- src/web/e-stepper/src/index.ts | 2 +- src/web/e-stepper/src/layout.ts | 48 +-- src/web/e-stepper/src/styles.ts | 6 +- src/web/e-stepper/wrap.mjs | 8 +- src/web/stepper/src/SubstVisualizer.tsx | 20 +- .../src/__tests__/SubstVisualizer.test.tsx | 112 +++--- src/web/stepper/src/__tests__/render.test.tsx | 348 ++++++++--------- src/web/stepper/src/render.tsx | 200 +++++----- 41 files changed, 1108 insertions(+), 1113 deletions(-) diff --git a/src/common/autocomplete/src/syntax.ts b/src/common/autocomplete/src/syntax.ts index d505d14..cd3c6b7 100644 --- a/src/common/autocomplete/src/syntax.ts +++ b/src/common/autocomplete/src/syntax.ts @@ -10,7 +10,9 @@ export interface SyntaxHighlightAck { } export type SyntaxHighlightMessage = - SyntaxHighlightRequest | SyntaxHighlightResponse | SyntaxHighlightAck; + | SyntaxHighlightRequest + | SyntaxHighlightResponse + | SyntaxHighlightAck; /** * References an implementation supplied by an existing Ace mode. diff --git a/src/common/cse-machine/src/index.ts b/src/common/cse-machine/src/index.ts index d4f4c60..b281699 100644 --- a/src/common/cse-machine/src/index.ts +++ b/src/common/cse-machine/src/index.ts @@ -181,7 +181,7 @@ export interface CseDiagramAnchor extends CseDiagramPoint { * drawn, e.g. when it is scrolled or collapsed out of the view. */ export type CseDiagramAnchorResolver = (target: { - kind: "object" | "frame"; + kind: 'object' | 'frame'; /** A {@link CseSerializedValue.objectId} or a {@link CseSerializedEnvFrame.id}. */ id: string; }) => CseDiagramAnchor | null; diff --git a/src/common/data-visualizer/src/index.ts b/src/common/data-visualizer/src/index.ts index 8e44107..8e36ab3 100644 --- a/src/common/data-visualizer/src/index.ts +++ b/src/common/data-visualizer/src/index.ts @@ -59,9 +59,9 @@ export type RefId = number; * distinguishes "pair" from "list" for classification purposes by `children.length`, not by tag. */ export type SerializedDataVisualizerNode = - | { type: "array"; refId: RefId; children: SerializedDataVisualizerNode[] } + | { type: 'array'; refId: RefId; children: SerializedDataVisualizerNode[] } | { - type: "empty"; + type: 'empty'; /** * How the language writes its empty value (e.g. `"None"` for Python), shown when the empty value * is drawn on its own. Inside a pair/array it is always drawn as a diagonal slash instead. Omit it @@ -69,9 +69,9 @@ export type SerializedDataVisualizerNode = */ displayValue?: string; } - | { type: "leaf"; displayValue: string; label: string } - | { type: "function"; refId: RefId; displayValue: string } - | { type: "ref"; refId: RefId }; + | { type: 'leaf'; displayValue: string; label: string } + | { type: 'function'; refId: RefId; displayValue: string } + | { type: 'ref'; refId: RefId }; /** One row = the fully-serialized arguments of a single `draw_data(...)` call. */ export type SerializedDataVisualizerRow = SerializedDataVisualizerNode[]; diff --git a/src/common/e-stepper/package.json b/src/common/e-stepper/package.json index e0b0a9c..e568d4a 100644 --- a/src/common/e-stepper/package.json +++ b/src/common/e-stepper/package.json @@ -6,8 +6,8 @@ "scripts": { "build": "rollup -c", "prepack": "yarn build", - "test": "jest", - "test-coverage": "jest --coverage" + "test": "vitest", + "test-coverage": "vitest --coverage" }, "license": "ISC", "files": [ diff --git a/src/common/e-stepper/rollup.config.mjs b/src/common/e-stepper/rollup.config.mjs index 05aaa22..aaf820c 100644 --- a/src/common/e-stepper/rollup.config.mjs +++ b/src/common/e-stepper/rollup.config.mjs @@ -1,25 +1,21 @@ // @ts-check -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; -import { defineConfig } from "rollup"; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], - plugins: [ - nodeResolve(), - typescript({ exclude: ['**/__tests__/**'] }), - terser() - ], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], }); diff --git a/src/common/e-stepper/src/__tests__/common.test.ts b/src/common/e-stepper/src/__tests__/common.test.ts index 4b9e855..672222c 100644 --- a/src/common/e-stepper/src/__tests__/common.test.ts +++ b/src/common/e-stepper/src/__tests__/common.test.ts @@ -1,5 +1,5 @@ -import { RUNNER_ID as STEPPER_RUNNER_ID, STEPPER_CHANNEL_ID } from "@sourceacademy/common-stepper"; -import { expect, test } from "vitest"; +import { RUNNER_ID as STEPPER_RUNNER_ID, STEPPER_CHANNEL_ID } from '@sourceacademy/common-stepper'; +import { expect, test } from 'vitest'; import { ENV_BLOCK_NODE_TYPE, @@ -11,77 +11,77 @@ import { isEnvBlockNode, isRefNode, type EStepperStep, -} from ".."; +} from '..'; -test("runner and web ids are distinct", () => { +test('runner and web ids are distinct', () => { expect(RUNNER_ID).not.toBe(WEB_ID); }); test("has stable ids, distinct from the stepper's", () => { - expect(E_STEPPER_CHANNEL_ID).toBe("__e_stepper"); - expect(E_STEPPER_DIRECTORY_ID).toBe("e-stepper"); + expect(E_STEPPER_CHANNEL_ID).toBe('__e_stepper'); + expect(E_STEPPER_DIRECTORY_ID).toBe('e-stepper'); expect(E_STEPPER_CHANNEL_ID).not.toBe(STEPPER_CHANNEL_ID); expect(RUNNER_ID).not.toBe(STEPPER_RUNNER_ID); }); -test("type guards recognise EnvBlock and Ref nodes", () => { - const ref = { type: REF_NODE_TYPE, nodeId: "1", objectId: "#1" }; - const block = { type: ENV_BLOCK_NODE_TYPE, nodeId: "2", envId: "E1", body: [ref] }; - const literal = { type: "Literal", nodeId: "3", value: 1 }; +test('type guards recognise EnvBlock and Ref nodes', () => { + const ref = { type: REF_NODE_TYPE, nodeId: '1', objectId: '#1' }; + const block = { type: ENV_BLOCK_NODE_TYPE, nodeId: '2', envId: 'E1', body: [ref] }; + const literal = { type: 'Literal', nodeId: '3', value: 1 }; expect(isRefNode(ref)).toBe(true); expect(isEnvBlockNode(block)).toBe(true); expect(isRefNode(literal)).toBe(false); expect(isEnvBlockNode(literal)).toBe(false); }); -test("a step survives structured cloning unchanged", () => { +test('a step survives structured cloning unchanged', () => { // The protocol's contract: everything crossing the channel is plain JSON. const step: EStepperStep = { ast: { - type: "Program", - nodeId: "0", + type: 'Program', + nodeId: '0', body: [ { type: ENV_BLOCK_NODE_TYPE, - nodeId: "1", - envId: "E1", - body: [{ type: REF_NODE_TYPE, nodeId: "2", objectId: "#2" }], + nodeId: '1', + envId: 'E1', + body: [{ type: REF_NODE_TYPE, nodeId: '2', objectId: '#2' }], }, ], }, - markers: [{ redexId: "1", redexType: "beforeMarker", explanation: "return withdraw" }], + markers: [{ redexId: '1', redexType: 'beforeMarker', explanation: 'return withdraw' }], frames: [ - { id: "Global", name: "global", parentId: null, isGarbage: false, bindings: [] }, + { id: 'Global', name: 'global', parentId: null, isGarbage: false, bindings: [] }, { - id: "E1", - name: "make_withdraw", - parentId: "Global", + id: 'E1', + name: 'make_withdraw', + parentId: 'Global', isGarbage: false, bindings: [ - { name: "balance", value: { kind: "primitive", display: "100", label: "int" } }, - { name: "withdraw", value: { kind: "ref", objectId: "#2" } }, + { name: 'balance', value: { kind: 'primitive', display: '100', label: 'int' } }, + { name: 'withdraw', value: { kind: 'ref', objectId: '#2' } }, ], }, ], heap: [ { - kind: "function", - id: "#2", - name: "withdraw", - params: ["amount"], - envId: "E1", - source: "def withdraw(amount): ...", + kind: 'function', + id: '#2', + name: 'withdraw', + params: ['amount'], + envId: 'E1', + source: 'def withdraw(amount): ...', isGarbage: false, }, { - kind: "list", - id: "#3", - elements: [{ kind: "builtin", name: "print" }, { kind: "unassigned" }], + kind: 'list', + id: '#3', + elements: [{ kind: 'builtin', name: 'print' }, { kind: 'unassigned' }], isGarbage: true, }, ], - activeFrameId: "E1", - lookups: [{ frameId: "E1", name: "withdraw" }], + activeFrameId: 'E1', + lookups: [{ frameId: 'E1', name: 'withdraw' }], }; expect(structuredClone(step)).toEqual(step); }); diff --git a/src/common/e-stepper/src/index.ts b/src/common/e-stepper/src/index.ts index 46f389e..f2e817c 100644 --- a/src/common/e-stepper/src/index.ts +++ b/src/common/e-stepper/src/index.ts @@ -24,38 +24,38 @@ * - {@link RefNode}: a reference to a heap object. */ -import type { CseSnapshot } from "@sourceacademy/common-cse-machine"; +import type { CseSnapshot } from '@sourceacademy/common-cse-machine'; import type { SerializedMarker, SerializedStepperNode, SerializedStepperStep, SyntaxProfile, -} from "@sourceacademy/common-stepper"; +} from '@sourceacademy/common-stepper'; /** The channel the e-stepper runner and host plugins communicate over. */ -export const E_STEPPER_CHANNEL_ID = "__e_stepper"; +export const E_STEPPER_CHANNEL_ID = '__e_stepper'; /** The id of the runner (worker-side) e-stepper plugin. */ -export const RUNNER_ID = "__runner_e_stepper"; +export const RUNNER_ID = '__runner_e_stepper'; /** The id of the web/host (browser-side) e-stepper plugin. */ -export const WEB_ID = "__web_e_stepper"; +export const WEB_ID = '__web_e_stepper'; /** * The id used to look the e-stepper up in the plugin directory (i.e. the argument to * `IRunnerPlugin.hostLoadPlugin`). The host resolves this to the web plugin's bundle URL. */ -export const E_STEPPER_DIRECTORY_ID = "e-stepper"; +export const E_STEPPER_DIRECTORY_ID = 'e-stepper'; /* -------------------------------------------------------------------------- */ /* Program nodes into the store */ /* -------------------------------------------------------------------------- */ /** The `type` of an {@link EnvBlockNode}. */ -export const ENV_BLOCK_NODE_TYPE = "EnvBlock"; +export const ENV_BLOCK_NODE_TYPE = 'EnvBlock'; /** The `type` of a {@link RefNode}. */ -export const REF_NODE_TYPE = "Ref"; +export const REF_NODE_TYPE = 'Ref'; /** * A function body under evaluation, together with the frame it is evaluated in. Created when a @@ -97,13 +97,13 @@ export type EStepperValue = * A primitive value, shown inline. `display` is the already-rendered text (e.g. `"3.0"`, * `"'hello'"`, `"None"`); `label` is a coarse type tag (e.g. `"int"`, `"float"`, `"str"`). */ - | { kind: "primitive"; display: string; label: string } + | { kind: 'primitive'; display: string; label: string } /** A reference to a heap object. */ - | { kind: "ref"; objectId: string } + | { kind: 'ref'; objectId: string } /** A builtin function, shown by its name (builtins are not heap objects and not drawn). */ - | { kind: "builtin"; name: string } + | { kind: 'builtin'; name: string } /** A binding that exists but has not been assigned yet (e.g. a function's local). */ - | { kind: "unassigned" }; + | { kind: 'unassigned' }; /** A name → value binding in a frame. */ export interface EStepperBinding { @@ -130,7 +130,7 @@ export interface EStepperFrame { /** A function object: what a function definition or lambda expression evaluates to. */ export interface EStepperFunctionObject { - kind: "function"; + kind: 'function'; /** Stable id for the whole run, unique among heap objects; also its label (e.g. `"#3"`). */ id: string; /** The function's name, or absent for an anonymous function (e.g. a lambda). */ @@ -146,7 +146,7 @@ export interface EStepperFunctionObject { /** A mutable sequence, e.g. a Python list. A pair is a list of two elements. */ export interface EStepperListObject { - kind: "list"; + kind: 'list'; /** Stable id for the whole run, unique among heap objects; also its label (e.g. `"#7"`). */ id: string; elements: EStepperValue[]; @@ -195,7 +195,7 @@ export type { SerializedMarker, SerializedStepperNode, SyntaxProfile }; /** Runner → host: the computed evaluation steps for the most recent run. */ export interface EStepperStepsMessage { - type: "steps"; + type: 'steps'; steps: EStepperStep[]; /** * The language's rendering rules for ordinary program nodes, as for the stepper. `EnvBlock` and @@ -206,13 +206,13 @@ export interface EStepperStepsMessage { /** Runner → host: stepping failed (e.g. a parse error). */ export interface EStepperErrorMessage { - type: "error"; + type: 'error'; error: string; } /** Host → runner: asks the runner to (re)send the steps it last computed. */ export interface EStepperRequestMessage { - type: "request"; + type: 'request'; } /** Every message that may cross the {@link E_STEPPER_CHANNEL_ID} channel. */ diff --git a/src/runner/e-stepper/package.json b/src/runner/e-stepper/package.json index 2818dc6..68f7646 100644 --- a/src/runner/e-stepper/package.json +++ b/src/runner/e-stepper/package.json @@ -6,8 +6,8 @@ "scripts": { "build": "rollup -c", "prepack": "yarn build", - "test": "jest", - "test-coverage": "jest --coverage" + "test": "vitest", + "test-coverage": "vitest --coverage" }, "license": "ISC", "files": [ diff --git a/src/runner/e-stepper/rollup.config.mjs b/src/runner/e-stepper/rollup.config.mjs index 05aaa22..aaf820c 100644 --- a/src/runner/e-stepper/rollup.config.mjs +++ b/src/runner/e-stepper/rollup.config.mjs @@ -1,25 +1,21 @@ // @ts-check -import nodeResolve from "@rollup/plugin-node-resolve"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; -import { defineConfig } from "rollup"; +import nodeResolve from '@rollup/plugin-node-resolve'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: [ { - file: "dist/index.cjs", - format: "cjs", + file: 'dist/index.cjs', + format: 'cjs', }, { - file: "dist/index.mjs", - format: "esm", + file: 'dist/index.mjs', + format: 'esm', }, ], - plugins: [ - nodeResolve(), - typescript({ exclude: ['**/__tests__/**'] }), - terser() - ], + plugins: [nodeResolve(), typescript({ exclude: ['**/__tests__/**'] }), terser()], }); diff --git a/src/runner/e-stepper/src/__tests__/runner.test.ts b/src/runner/e-stepper/src/__tests__/runner.test.ts index 62cb743..28ad0c0 100644 --- a/src/runner/e-stepper/src/__tests__/runner.test.ts +++ b/src/runner/e-stepper/src/__tests__/runner.test.ts @@ -1,13 +1,12 @@ - import { E_STEPPER_CHANNEL_ID, RUNNER_ID, type EStepperStep, -} from "@sourceacademy/common-e-stepper"; -import { STEPPER_CHANNEL_ID } from "@sourceacademy/common-stepper"; -import { BaseStepperRunnerPlugin } from "@sourceacademy/runner-stepper"; -import { expect, test } from "vitest"; -import { BaseEStepperRunnerPlugin } from ".."; +} from '@sourceacademy/common-e-stepper'; +import { STEPPER_CHANNEL_ID } from '@sourceacademy/common-stepper'; +import { BaseStepperRunnerPlugin } from '@sourceacademy/runner-stepper'; +import { expect, test } from 'vitest'; +import { BaseEStepperRunnerPlugin } from '..'; class FakeChannel { name = E_STEPPER_CHANNEL_ID; @@ -29,10 +28,10 @@ class FakeChannel { } const step = (i: number): EStepperStep => ({ - ast: { type: "Literal", nodeId: String(i), value: i }, - frames: [{ id: "Global", name: "global", parentId: null, bindings: [], isGarbage: false }], + ast: { type: 'Literal', nodeId: String(i), value: i }, + frames: [{ id: 'Global', name: 'global', parentId: null, bindings: [], isGarbage: false }], heap: [], - activeFrameId: "Global", + activeFrameId: 'Global', }); // A trivial concrete e-stepper: every "AST" (a number n) becomes n steps. @@ -43,7 +42,6 @@ class CountingEStepper extends BaseEStepperRunnerPlugin { } const make = (Cls: new (...args: never[]) => T, channel: FakeChannel): T => - new (Cls as any)({} as any, [channel as any]); test("attaches to the e-stepper channel, not the stepper's", () => { @@ -52,16 +50,16 @@ test("attaches to the e-stepper channel, not the stepper's", () => { expect(BaseStepperRunnerPlugin.channelAttach).toEqual([STEPPER_CHANNEL_ID]); }); -test("has the e-stepper runner id", () => { +test('has the e-stepper runner id', () => { expect(make(CountingEStepper, new FakeChannel()).id).toBe(RUNNER_ID); }); -test("sendSteps computes, caches and pushes steps; request replays them", async () => { +test('sendSteps computes, caches and pushes steps; request replays them', async () => { const channel = new FakeChannel(); const plugin = make(CountingEStepper, channel); await plugin.sendSteps(2); - expect(channel.sent).toEqual([{ type: "steps", steps: [step(0), step(1)] }]); - channel.emit({ type: "request" }); + expect(channel.sent).toEqual([{ type: 'steps', steps: [step(0), step(1)] }]); + channel.emit({ type: 'request' }); expect(channel.sent).toHaveLength(2); expect(channel.sent[1]).toEqual(channel.sent[0]); }); @@ -70,22 +68,22 @@ test("ships the language's syntax profile with the steps", async () => { const channel = new FakeChannel(); class WithProfile extends CountingEStepper { protected override getSyntaxProfile() { - return { templates: { Literal: [{ prop: "value" }] } }; + return { templates: { Literal: [{ prop: 'value' }] } }; } } await make(WithProfile, channel).sendSteps(1); expect(channel.sent).toEqual([ - { type: "steps", steps: [step(0)], profile: { templates: { Literal: [{ prop: "value" }] } } }, + { type: 'steps', steps: [step(0)], profile: { templates: { Literal: [{ prop: 'value' }] } } }, ]); }); -test("sendSteps reports errors instead of throwing", async () => { +test('sendSteps reports errors instead of throwing', async () => { const channel = new FakeChannel(); class Boom extends BaseEStepperRunnerPlugin { getSteps(): never { - throw new Error("kaboom"); + throw new Error('kaboom'); } } await make(Boom, channel).sendSteps(1); - expect(channel.sent).toEqual([{ type: "error", error: "kaboom" }]); + expect(channel.sent).toEqual([{ type: 'error', error: 'kaboom' }]); }); diff --git a/src/runner/e-stepper/src/index.ts b/src/runner/e-stepper/src/index.ts index 8ac25b9..5eac8ea 100644 --- a/src/runner/e-stepper/src/index.ts +++ b/src/runner/e-stepper/src/index.ts @@ -2,8 +2,8 @@ import { E_STEPPER_CHANNEL_ID, RUNNER_ID, type EStepperStep, -} from "@sourceacademy/common-e-stepper"; -import { BaseStepperRunnerPlugin } from "@sourceacademy/runner-stepper"; +} from '@sourceacademy/common-e-stepper'; +import { BaseStepperRunnerPlugin } from '@sourceacademy/runner-stepper'; /** * The language-agnostic runner half of the e-stepper (environment stepper). diff --git a/src/web/data-visualizer/src/__tests__/classify.test.ts b/src/web/data-visualizer/src/__tests__/classify.test.ts index 67a63cc..b45604f 100644 --- a/src/web/data-visualizer/src/__tests__/classify.test.ts +++ b/src/web/data-visualizer/src/__tests__/classify.test.ts @@ -171,33 +171,33 @@ test('a general tree whose head element is itself an improper (non-general-tree) const llist = (...elements: SerializedDataVisualizerNode[]): SerializedDataVisualizerNode => elements.reduceRight((acc, e) => pair(e, acc), empty()); const fn = (): SerializedDataVisualizerNode => ({ - type: "function", + type: 'function', refId: nextRefId++, - displayValue: "", + displayValue: '', }); -test("a list of functions is a general tree with functions as data items (issue #113)", () => { +test('a list of functions is a general tree with functions as data items (issue #113)', () => { // draw_data(llist(lambda x : x, lambda y: y, lambda z: z)) const result = classify(llist(fn(), fn(), fn())); expect(result.isGeneralTree).toBe(true); expect(result.layout).toBeDefined(); }); -test("functions still disqualify Binary Tree View", () => { +test('functions still disqualify Binary Tree View', () => { const result = classify(pair(fn(), pair(empty(), pair(empty(), empty())))); expect(result.isBinaryTree).toBe(false); }); -test("a general tree may mix functions with other data, at any depth", () => { +test('a general tree may mix functions with other data, at any depth', () => { const result = classify(llist(leaf(1), llist(fn(), leaf(2)), fn())); expect(result.isGeneralTree).toBe(true); }); -test("the same function appearing twice is repeated data, not shared structure", () => { +test('the same function appearing twice is repeated data, not shared structure', () => { const f = fn(); const ref: SerializedDataVisualizerNode = { - type: "ref", - refId: (f as Extract).refId, + type: 'ref', + refId: (f as Extract).refId, }; const result = classify(llist(f, ref)); expect(result.isSharedStructure).toBe(false); @@ -205,18 +205,18 @@ test("the same function appearing twice is repeated data, not shared structure", expect(result.isGeneralTree).toBe(true); }); -test("a shared pair is still shared structure, even alongside functions", () => { +test('a shared pair is still shared structure, even alongside functions', () => { const shared = pair(leaf(1), empty()); const ref: SerializedDataVisualizerNode = { - type: "ref", - refId: (shared as Extract).refId, + type: 'ref', + refId: (shared as Extract).refId, }; const result = classify(llist(fn(), shared, ref)); expect(result.isSharedStructure).toBe(true); expect(result.isGeneralTree).toBe(false); }); -test("a binary-tree-shaped node whose data is itself a list is not a binary tree (issue #84, case 5)", () => { +test('a binary-tree-shaped node whose data is itself a list is not a binary tree (issue #84, case 5)', () => { // draw_data([[1, [None, [None, None]]], [None, [None, None]]]) const result = classify(pair(binaryNode(1), pair(empty(), pair(empty(), empty())))); expect(result.isBinaryTree).toBe(false); diff --git a/src/web/data-visualizer/src/__tests__/format.test.ts b/src/web/data-visualizer/src/__tests__/format.test.ts index 10e9c74..19196c6 100644 --- a/src/web/data-visualizer/src/__tests__/format.test.ts +++ b/src/web/data-visualizer/src/__tests__/format.test.ts @@ -1,19 +1,19 @@ -import { describe, expect, test } from "vitest"; +import { describe, expect, test } from 'vitest'; -import { formatRoot } from "../format"; -import { DataTreeNode } from "../tree/DataTreeNode"; +import { formatRoot } from '../format'; +import { DataTreeNode } from '../tree/DataTreeNode'; describe(formatRoot, () => { test("shows the language's own spelling of an empty value drawn on its own", () => { - expect(formatRoot(DataTreeNode.empty("None"))).toBe("None"); + expect(formatRoot(DataTreeNode.empty('None'))).toBe('None'); }); - test("falls back to null when the language sends no spelling", () => { - expect(formatRoot(DataTreeNode.empty())).toBe("null"); + test('falls back to null when the language sends no spelling', () => { + expect(formatRoot(DataTreeNode.empty())).toBe('null'); }); - test("formats a leaf as before", () => { - expect(formatRoot(DataTreeNode.leaf("hi", "string"))).toBe('"hi"'); - expect(formatRoot(DataTreeNode.leaf("42", "number"))).toBe("42"); + test('formats a leaf as before', () => { + expect(formatRoot(DataTreeNode.leaf('hi', 'string'))).toBe('"hi"'); + expect(formatRoot(DataTreeNode.leaf('42', 'number'))).toBe('42'); }); }); diff --git a/src/web/data-visualizer/src/__tests__/treeViews.test.ts b/src/web/data-visualizer/src/__tests__/treeViews.test.ts index fce4ba8..4a7c6fc 100644 --- a/src/web/data-visualizer/src/__tests__/treeViews.test.ts +++ b/src/web/data-visualizer/src/__tests__/treeViews.test.ts @@ -48,7 +48,7 @@ vi.mock(import('react-konva'), () => { } as any; }); -const empty = (): SerializedDataVisualizerNode => ({ type: "empty" }); +const empty = (): SerializedDataVisualizerNode => ({ type: 'empty' }); const leaf = (n: number): SerializedDataVisualizerNode => ({ type: 'leaf', displayValue: String(n), @@ -151,11 +151,11 @@ describe(BinaryTreeDrawer, () => { }); test("an empty value carries the language's spelling of it through to the root node", () => { - const tree = Tree.fromSerializedNode({ type: "empty", displayValue: "None" }); - expect((tree.rootNode as DataTreeNode).displayValue).toBe("None"); + const tree = Tree.fromSerializedNode({ type: 'empty', displayValue: 'None' }); + expect((tree.rootNode as DataTreeNode).displayValue).toBe('None'); }); - test("the bare empty terminator is a trivial valid binary tree, drawn via the measured-text branch", () => { + test('the bare empty terminator is a trivial valid binary tree, drawn via the measured-text branch', () => { // Per isBinaryTreeNode: a binary tree is `null`, or a 3-element list — a bare *leaf* value is // neither, so (unlike this case) it fails classification and hits the warning box instead. const tree = Tree.fromSerializedNode(empty()); @@ -219,22 +219,22 @@ describe(BinaryTreeDrawer, () => { }); }); -describe("BinaryTreeDrawer canvas (issue #84)", () => { +describe('BinaryTreeDrawer canvas (issue #84)', () => { // Python value -> wire node, the way py-slang serializes it (2-element list -> pair). type Py = null | number | string | Py[]; const py = (v: Py): SerializedDataVisualizerNode => v === null ? empty() : Array.isArray(v) - ? { type: "array", refId: nextRefId++, children: v.map(py) } - : typeof v === "number" + ? { type: 'array', refId: nextRefId++, children: v.map(py) } + : typeof v === 'number' ? leaf(v) - : { type: "leaf", displayValue: v, label: "string" }; + : { type: 'leaf', displayValue: v, label: 'string' }; const N = null; test.each<[string, Py]>([ [ - "left-skewed (case 1)", + 'left-skewed (case 1)', [ 1, [ @@ -256,13 +256,13 @@ describe("BinaryTreeDrawer canvas (issue #84)", () => { ], ], [ - "right-skewed (case 2)", - ["A", [N, [["B", [N, [["C", [N, [["D", [N, [N, N]]], N]]], N]]], N]]], + 'right-skewed (case 2)', + ['A', [N, [['B', [N, [['C', [N, [['D', [N, [N, N]]], N]]], N]]], N]]], ], - ["right child only (case 3)", [10, [N, [[15, [N, [N, N]]], N]]]], - ])("every box of a %s tree is drawn inside the canvas", (_: string, value: Py) => { + ['right child only (case 3)', [10, [N, [[15, [N, [N, N]]], N]]]], + ])('every box of a %s tree is drawn inside the canvas', (_: string, value: Py) => { const tree = Tree.fromSerializedNode(py(value)); - const drawer = tree.draw("binaryTree") as BinaryTreeDrawer; + const drawer = tree.draw('binaryTree') as BinaryTreeDrawer; const stage = drawer.draw(0, 0, 0) as React.ReactElement<{ width: number; height: number; @@ -291,7 +291,7 @@ describe("BinaryTreeDrawer canvas (issue #84)", () => { }); }); -describe("BinaryTreeDrawer canvas margin", () => { +describe('BinaryTreeDrawer canvas margin', () => { // DataVisualizerView draws with a 1px margin (Config.StrokeWidth / 2), not 0. With a margin the // root's data slot is also "to the right of" runningX2's initial 0, so it must not be counted as // a right branch: that would widen even a single node's canvas by hundreds of pixels. @@ -302,16 +302,16 @@ describe("BinaryTreeDrawer canvas margin", () => { right: SerializedDataVisualizerNode = empty(), ): SerializedDataVisualizerNode => pair(leaf1(), pair(left, pair(right, empty()))); const widthAt = (value: SerializedDataVisualizerNode, m: number): number => { - const drawer = Tree.fromSerializedNode(value).draw("binaryTree") as BinaryTreeDrawer; + const drawer = Tree.fromSerializedNode(value).draw('binaryTree') as BinaryTreeDrawer; return (drawer.draw(m, m, 0) as React.ReactElement<{ width: number }>).props.width; }; test.each<[string, () => SerializedDataVisualizerNode]>([ - ["a single node", () => node()], - ["a node with a right child", () => node(empty(), node())], - ["a node with a left child", () => node(node())], + ['a single node', () => node()], + ['a node with a right child', () => node(empty(), node())], + ['a node with a left child', () => node(node())], ])( - "%s: the canvas grows only by the margin, not by a phantom branch", + '%s: the canvas grows only by the margin, not by a phantom branch', (_: string, make: () => SerializedDataVisualizerNode) => { expect(widthAt(make(), margin)).toBe(widthAt(make(), 0) + margin * 2); }, @@ -319,15 +319,15 @@ describe("BinaryTreeDrawer canvas margin", () => { }); describe(GeneralTreeDrawer, () => { - test("draws a list of functions as a tree with three data items, not the warning box (issue #113)", () => { + test('draws a list of functions as a tree with three data items, not the warning box (issue #113)', () => { // draw_data(llist(lambda x : x, lambda y: y, lambda z: z)) const fn = (): SerializedDataVisualizerNode => ({ - type: "function", + type: 'function', refId: nextRefId++, - displayValue: "", + displayValue: '', }); const tree = Tree.fromSerializedNode(pair(fn(), pair(fn(), pair(fn(), empty())))); - const drawer = tree.draw("generalTree") as GeneralTreeDrawer; + const drawer = tree.draw('generalTree') as GeneralTreeDrawer; const element = drawer.draw(0, 0, 0) as React.ReactElement<{ width: number }>; expect(element.props.width).not.toBe(445); const drawn = internals(drawer).drawables; @@ -335,17 +335,17 @@ describe(GeneralTreeDrawer, () => { }); test.each([ - ["llist(f, f, f)", "flat"], - ["llist(llist(1, 2), f)", "nestedFirst"], - ["llist(1, llist(f, 2), f)", "mixed"], - ["llist(llist(llist(f)), f, llist(f, f))", "deep"], + ['llist(f, f, f)', 'flat'], + ['llist(llist(1, 2), f)', 'nestedFirst'], + ['llist(1, llist(f, 2), f)', 'mixed'], + ['llist(llist(llist(f)), f, llist(f, f))', 'deep'], ])( - "functions in %s are drawn inside the canvas without overlapping anything", + 'functions in %s are drawn inside the canvas without overlapping anything', (_: string, shape: string) => { const fn = (): SerializedDataVisualizerNode => ({ - type: "function", + type: 'function', refId: nextRefId++, - displayValue: "", + displayValue: '', }); const llist = (...elements: SerializedDataVisualizerNode[]): SerializedDataVisualizerNode => elements.reduceRight((acc, e) => pair(e, acc), empty()); @@ -356,7 +356,7 @@ describe(GeneralTreeDrawer, () => { deep: () => llist(llist(llist(fn())), fn(), llist(fn(), fn())), }; const tree = Tree.fromSerializedNode(shapes[shape]()); - const drawer = tree.draw("generalTree") as GeneralTreeDrawer; + const drawer = tree.draw('generalTree') as GeneralTreeDrawer; const stage = drawer.draw(0, 0, 0) as React.ReactElement<{ width: number; height: number }>; // Every drawn pair (two boxes wide) and function glyph (two large circles wide), with its extent. @@ -404,7 +404,7 @@ describe(GeneralTreeDrawer, () => { }, ); - test("a non-general-tree structure draws the fixed-size warning box instead of a tree", () => { + test('a non-general-tree structure draws the fixed-size warning box instead of a tree', () => { // An *improper* list (here, a bare 2-tuple whose second slot is a leaf rather than another pair // or the empty terminator) is the only shape General Tree View actually rejects — a binary-tree- // shaped input is itself a valid (proper-list) general tree, see classify.test.ts. diff --git a/src/web/data-visualizer/src/classify.ts b/src/web/data-visualizer/src/classify.ts index 3520182..f925660 100644 --- a/src/web/data-visualizer/src/classify.ts +++ b/src/web/data-visualizer/src/classify.ts @@ -24,7 +24,7 @@ import type { RefId, SerializedDataVisualizerNode } from '@sourceacademy/common- /** A node's "type" for homogeneity comparisons — the closest equivalent to the old code's `typeof`. */ function typeLabel(node: SerializedDataVisualizerNode): string { - return node.type === "leaf" ? node.label : node.type; + return node.type === 'leaf' ? node.label : node.type; } /** True for a node that can be a pair-chain link: an `"array"` with exactly two children. A native @@ -56,7 +56,7 @@ function detectCyclesAndSharing(root: SerializedDataVisualizerNode): { function visit(node: SerializedDataVisualizerNode): void { switch (node.type) { - case "ref": + case 'ref': if (functionRefIds.has(node.refId)) { return; } @@ -72,11 +72,11 @@ function detectCyclesAndSharing(root: SerializedDataVisualizerNode): { } onPath.delete(node.refId); return; - case "function": + case 'function': functionRefIds.add(node.refId); return; - case "leaf": - case "empty": + case 'leaf': + case 'empty': return; } } @@ -106,7 +106,7 @@ function isBinaryTreeNode(node: SerializedDataVisualizerNode, rootLabel: string // Binary Tree View draws a node's data inside its box and hangs only the left/right subtrees // below it, so it has nowhere to put a compound (list) entry - it would be drawn as if it were a // branch. Such a tree is shown in General Tree View instead. - if (data.type === "array") { + if (data.type === 'array') { return false; } if (!isPairNode(rest)) { @@ -241,7 +241,7 @@ function computeLayout(root: SerializedDataVisualizerNode): TreeLayout { // same counter as the pairs there, so it neither overlaps a nested list's boxes nor falls below // the canvas. const head = node.children[0]; - if (head.type === "function") { + if (head.type === 'function') { const fnDepth = depth + 1; if (nodeCountByDepth[fnDepth] === undefined) { nodeCountByDepth[fnDepth] = 0; diff --git a/src/web/data-visualizer/src/format.ts b/src/web/data-visualizer/src/format.ts index b25c824..4a38d9f 100644 --- a/src/web/data-visualizer/src/format.ts +++ b/src/web/data-visualizer/src/format.ts @@ -1,5 +1,5 @@ -import { Config } from "./Config"; -import type { DataTreeNode } from "./tree/DataTreeNode"; +import { Config } from './Config'; +import type { DataTreeNode } from './tree/DataTreeNode'; /** * Formats a leaf's display value for showing inside a box: quotes and truncates strings (mirroring @@ -21,5 +21,5 @@ export function formatLeaf(displayValue: string, label: string): string { * empty value if it sent one (e.g. `None` for Python), `null` otherwise, or the formatted leaf. */ export function formatRoot(node: DataTreeNode): string { - return node.isEmpty ? (node.displayValue ?? "null") : formatLeaf(node.displayValue!, node.label!); + return node.isEmpty ? (node.displayValue ?? 'null') : formatLeaf(node.displayValue!, node.label!); } diff --git a/src/web/data-visualizer/src/tree/BinaryTreeDrawer.tsx b/src/web/data-visualizer/src/tree/BinaryTreeDrawer.tsx index 519f454..bcf2e10 100644 --- a/src/web/data-visualizer/src/tree/BinaryTreeDrawer.tsx +++ b/src/web/data-visualizer/src/tree/BinaryTreeDrawer.tsx @@ -1,13 +1,13 @@ import Konva from 'konva'; import { Layer, Stage, Text } from 'react-konva'; -import { Config } from "../Config"; -import type { ClassificationResult } from "../classify"; -import { ArrowDrawable, BackwardArrowDrawable } from "../drawable/Drawable"; -import { formatRoot } from "../format"; -import { AlreadyParsedTreeNode } from "./AlreadyParsedTreeNode"; -import { ClassicDrawer } from "./ClassicDrawer"; -import type { Tree } from "./Tree"; +import { Config } from '../Config'; +import type { ClassificationResult } from '../classify'; +import { ArrowDrawable, BackwardArrowDrawable } from '../drawable/Drawable'; +import { formatRoot } from '../format'; +import { AlreadyParsedTreeNode } from './AlreadyParsedTreeNode'; +import { ClassicDrawer } from './ClassicDrawer'; +import type { Tree } from './Tree'; import { ArrayTreeNode, DataTreeNode, diff --git a/src/web/data-visualizer/src/tree/ClassicDrawer.tsx b/src/web/data-visualizer/src/tree/ClassicDrawer.tsx index f2dd365..19eddc0 100644 --- a/src/web/data-visualizer/src/tree/ClassicDrawer.tsx +++ b/src/web/data-visualizer/src/tree/ClassicDrawer.tsx @@ -1,11 +1,11 @@ import Konva from 'konva'; import { Layer, Stage, Text } from 'react-konva'; -import { Config } from "../Config"; -import { ArrowDrawable, BackwardArrowDrawable } from "../drawable/Drawable"; -import { formatRoot } from "../format"; -import { AlreadyParsedTreeNode } from "./AlreadyParsedTreeNode"; -import type { Tree } from "./Tree"; +import { Config } from '../Config'; +import { ArrowDrawable, BackwardArrowDrawable } from '../drawable/Drawable'; +import { formatRoot } from '../format'; +import { AlreadyParsedTreeNode } from './AlreadyParsedTreeNode'; +import type { Tree } from './Tree'; import { ArrayTreeNode, DataTreeNode, diff --git a/src/web/data-visualizer/src/tree/GeneralTreeDrawer.tsx b/src/web/data-visualizer/src/tree/GeneralTreeDrawer.tsx index 8b3dcd4..c35e8e8 100644 --- a/src/web/data-visualizer/src/tree/GeneralTreeDrawer.tsx +++ b/src/web/data-visualizer/src/tree/GeneralTreeDrawer.tsx @@ -1,13 +1,13 @@ import Konva from 'konva'; import { Layer, Stage, Text } from 'react-konva'; -import { Config } from "../Config"; -import type { ClassificationResult } from "../classify"; -import { ArrowDrawable, BackwardArrowDrawable } from "../drawable/Drawable"; -import { formatRoot } from "../format"; -import { AlreadyParsedTreeNode } from "./AlreadyParsedTreeNode"; -import { ClassicDrawer } from "./ClassicDrawer"; -import type { Tree } from "./Tree"; +import { Config } from '../Config'; +import type { ClassificationResult } from '../classify'; +import { ArrowDrawable, BackwardArrowDrawable } from '../drawable/Drawable'; +import { formatRoot } from '../format'; +import { AlreadyParsedTreeNode } from './AlreadyParsedTreeNode'; +import { ClassicDrawer } from './ClassicDrawer'; +import type { Tree } from './Tree'; import { ArrayTreeNode, DataTreeNode, diff --git a/src/web/data-visualizer/src/tree/Tree.ts b/src/web/data-visualizer/src/tree/Tree.ts index a4da361..8a06643 100644 --- a/src/web/data-visualizer/src/tree/Tree.ts +++ b/src/web/data-visualizer/src/tree/Tree.ts @@ -71,9 +71,9 @@ export class Tree { // the whole tab. return already ? new AlreadyParsedTreeNode(already) : DataTreeNode.empty(); } - case "empty": + case 'empty': return DataTreeNode.empty(node.displayValue); - case "leaf": + case 'leaf': return DataTreeNode.leaf(node.displayValue, node.label); case 'array': { const treeNode = new ArrayTreeNode(); diff --git a/src/web/e-stepper/package.json b/src/web/e-stepper/package.json index 321971d..525d5d5 100644 --- a/src/web/e-stepper/package.json +++ b/src/web/e-stepper/package.json @@ -5,7 +5,8 @@ "description": "Host (web) plugin for the Source Academy environment stepper (e-stepper): renders the program and its environment diagram", "scripts": { "build": "rollup -c && node wrap.mjs", - "prepack": "yarn build" + "prepack": "yarn build", + "test": "vitest" }, "license": "ISC", "files": [ diff --git a/src/web/e-stepper/rollup.config.mjs b/src/web/e-stepper/rollup.config.mjs index d8c56c2..d80f847 100644 --- a/src/web/e-stepper/rollup.config.mjs +++ b/src/web/e-stepper/rollup.config.mjs @@ -1,11 +1,11 @@ // @ts-check -import commonjs from "@rollup/plugin-commonjs"; -import nodeResolve from "@rollup/plugin-node-resolve"; -import replace from "@rollup/plugin-replace"; -import terser from "@rollup/plugin-terser"; -import typescript from "@rollup/plugin-typescript"; -import { defineConfig } from "rollup"; +import commonjs from '@rollup/plugin-commonjs'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import replace from '@rollup/plugin-replace'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; +import { defineConfig } from 'rollup'; // React, Blueprint, and Konva/react-konva are provided by the host at load time through the // require-provider (see the frontend's `requireProvider`, which already exposes them for other @@ -15,27 +15,27 @@ import { defineConfig } from "rollup"; // dispatcher at runtime. Everything else (conductor conduit, the common e-stepper protocol, and the // stepper's program renderer from ../stepper/src) is bundled. wrap.mjs then wraps the CJS output into the host's factory contract. const external = [ - "react", - "react-dom", - "react/jsx-runtime", - "react/jsx-dev-runtime", - "@blueprintjs/core", - "konva", - "react-konva", + 'react', + 'react-dom', + 'react/jsx-runtime', + 'react/jsx-dev-runtime', + '@blueprintjs/core', + 'konva', + 'react-konva', ]; export default defineConfig({ - input: "src/index.ts", + input: 'src/index.ts', output: { - file: "dist/index.cjs", - format: "cjs", - exports: "default", + file: 'dist/index.cjs', + format: 'cjs', + exports: 'default', }, external, plugins: [ // konva/react-konva pull in CJS deps (e.g. react-is) that branch on `process.env.NODE_ENV` at // module-init time; there's no `process` global in the browser, so replace it at build time. - replace({ preventAssignment: true, "process.env.NODE_ENV": JSON.stringify("production") }), + replace({ preventAssignment: true, 'process.env.NODE_ENV': JSON.stringify('production') }), nodeResolve({ browser: true, preferBuiltins: false }), commonjs(), typescript({ exclude: ['**/__tests__/**'] }), diff --git a/src/web/e-stepper/src/EStepperHostPlugin.tsx b/src/web/e-stepper/src/EStepperHostPlugin.tsx index 83827b0..9d4b68a 100644 --- a/src/web/e-stepper/src/EStepperHostPlugin.tsx +++ b/src/web/e-stepper/src/EStepperHostPlugin.tsx @@ -1,24 +1,24 @@ -import type { ICseDiagramService, IHostServices } from "@sourceacademy/common-cse-machine"; +import type { ICseDiagramService, IHostServices } from '@sourceacademy/common-cse-machine'; import { E_STEPPER_CHANNEL_ID, WEB_ID, type EStepperMessage, type EStepperStep, type SyntaxProfile, -} from "@sourceacademy/common-e-stepper"; -import type { ITabService, Tab } from "@sourceacademy/common-tabs"; +} from '@sourceacademy/common-e-stepper'; +import type { ITabService, Tab } from '@sourceacademy/common-tabs'; import { checkIsPluginClass, type IChannel, type IConduit, type IPlugin, -} from "@sourceacademy/conductor/conduit"; -import { createElement, useSyncExternalStore } from "react"; +} from '@sourceacademy/conductor/conduit'; +import { createElement, useSyncExternalStore } from 'react'; -import EStepperView, { type PaneLayout } from "./EStepperView"; +import EStepperView, { type PaneLayout } from './EStepperView'; /** The side-content tab id used by the host to show/hide the e-stepper tab. */ -const TAB_ID = "e-stepper"; +const TAB_ID = 'e-stepper'; /** * The user's arrangement of the panes, kept on `globalThis` for the page. Neither the plugin nor this @@ -26,7 +26,7 @@ const TAB_ID = "e-stepper"; * bundle is wrapped in a factory (see `wrap.mjs`) that the host calls again each time, so even a * module-level variable is new on every Run. The divider is to stay where the user put it. */ -const LAYOUT_KEY = Symbol.for("sourceacademy.e-stepper.layout"); +const LAYOUT_KEY = Symbol.for('sourceacademy.e-stepper.layout'); const layout: PaneLayout = ((globalThis as Record)[LAYOUT_KEY] ??= {}); interface State { @@ -61,16 +61,16 @@ export class EStepperHostPlugin implements IPlugin { ) { this.__channel = channel; this.__channel.subscribe(message => { - if (message.type === "steps") { + if (message.type === 'steps') { this.__state = { steps: message.steps, profile: message.profile, error: null }; this.__emit(); - } else if (message.type === "error") { + } else if (message.type === 'error') { this.__state = { steps: [], error: message.error }; this.__emit(); } }); // Ask the runner to replay any steps it already computed (e.g. tab opened after a run). - this.__channel.send({ type: "request" }); + this.__channel.send({ type: 'request' }); const subscribe = (listener: () => void) => this.subscribe(listener); const getState = () => this.getState(); @@ -88,9 +88,9 @@ export class EStepperHostPlugin implements IPlugin { const tab: Tab = { id: TAB_ID, - label: "E-Stepper", + label: 'E-Stepper', // The Stepper's icon: the two never appear together (Stepper: Python §1-2, E-Stepper: §3-4). - iconName: "flow-review", + iconName: 'flow-review', body: createElement(EStepperTab), }; tabService.registerTab(tab); diff --git a/src/web/e-stepper/src/EStepperView.tsx b/src/web/e-stepper/src/EStepperView.tsx index b2499d0..9b7b53f 100644 --- a/src/web/e-stepper/src/EStepperView.tsx +++ b/src/web/e-stepper/src/EStepperView.tsx @@ -8,27 +8,27 @@ import { Pre, Slider, Switch, -} from "@blueprintjs/core"; +} from '@blueprintjs/core'; import type { CseDiagramAnchorResolver, CseSnapshot, ICseDiagramService, -} from "@sourceacademy/common-cse-machine"; +} from '@sourceacademy/common-cse-machine'; import type { EStepperHeapObject, EStepperStep, SyntaxProfile, -} from "@sourceacademy/common-e-stepper"; -import classNames from "classnames"; -import { useCallback, useEffect, useMemo, useState } from "react"; +} from '@sourceacademy/common-e-stepper'; +import classNames from 'classnames'; +import { useCallback, useEffect, useMemo, useState } from 'react'; -import { CustomASTRenderer, type NodeRenderers, type StepperNode } from "../../stepper/src/render"; -import { sliderLabels } from "../../stepper/src/sliderLabels"; -import { injectStepperStyles } from "../../stepper/src/styles"; -import EnvDiagram from "./EnvDiagram"; -import ProgramArrows, { ENV_ATTRIBUTE, REF_ATTRIBUTE } from "./ProgramArrows"; -import { frameColor } from "./colors"; -import { injectEStepperStyles } from "./styles"; +import { CustomASTRenderer, type NodeRenderers, type StepperNode } from '../../stepper/src/render'; +import { sliderLabels } from '../../stepper/src/sliderLabels'; +import { injectStepperStyles } from '../../stepper/src/styles'; +import EnvDiagram from './EnvDiagram'; +import ProgramArrows, { ENV_ATTRIBUTE, REF_ATTRIBUTE } from './ProgramArrows'; +import { frameColor } from './colors'; +import { injectEStepperStyles } from './styles'; const DEFAULT_PROGRAM_HEIGHT = 240; /** The least height of the diagram below the program, and of the program: about one line of it. */ @@ -75,7 +75,7 @@ function useRemembered( const set = useCallback( (update: React.SetStateAction>) => setValue(old => { - const next = typeof update === "function" ? update(old) : update; + const next = typeof update === 'function' ? update(old) : update; if (layout) layout[key] = next; return next; }), @@ -86,7 +86,7 @@ function useRemembered( /** Whether the step is a stop for breakpoint navigation: it evaluates a `breakpoint()` statement. */ const isBreakpoint = (step: EStepperStep): boolean => - step.markers?.some(m => m.redexNodeType === "DebuggerStatement") ?? false; + step.markers?.some(m => m.redexNodeType === 'DebuggerStatement') ?? false; /** The step value (1-based) of the next breakpoint after `value`, or of the last step. */ export function nextBreakpoint(steps: EStepperStep[], value: number): number { @@ -103,7 +103,7 @@ export function previousBreakpoint(steps: EStepperStep[], value: number): number } /** The guide for students, built from `docs/md/README_ESTEPPER.md` of py-slang. */ -const GUIDE_URL = "https://docs.sourceacademy.org/python/e-stepper/"; +const GUIDE_URL = 'https://docs.sourceacademy.org/python/e-stepper/'; function DefaultText() { return ( @@ -119,7 +119,7 @@ function DefaultText() { bracketed body to find its frame.

- What do the brackets, labels and badges mean? Read{" "} + What do the brackets, labels and badges mean? Read{' '} the guide to the environment stepper @@ -140,7 +140,7 @@ function DefaultText() { * a height, that would make the explanation as tall as the tab. Inline, so it outranks any * stylesheet. */ -const CARD_STYLE: React.CSSProperties = { height: "auto", flex: "0 0 auto" }; +const CARD_STYLE: React.CSSProperties = { height: 'auto', flex: '0 0 auto' }; /** The least height of the tab, and the gap it leaves below itself in the browser window. */ const MIN_TAB_HEIGHT = 400; @@ -154,7 +154,7 @@ const BOTTOM_GAP = 16; * ask (no DOM), the fixed gap. */ function bottomInset(element: HTMLElement): number { - if (typeof getComputedStyle !== "function") return BOTTOM_GAP; + if (typeof getComputedStyle !== 'function') return BOTTOM_GAP; let inset = 0; for (let e: HTMLElement | null = element; e && e !== document.body; e = e.parentElement) { const style = getComputedStyle(e); @@ -176,7 +176,7 @@ function bottomInset(element: HTMLElement): number { function useFillHeight(element: HTMLElement | null): number | undefined { const [height, setHeight] = useState(undefined); const fit = useCallback(() => { - if (!element || typeof window === "undefined") return; + if (!element || typeof window === 'undefined') return; const top = element.getBoundingClientRect().top; if (!Number.isFinite(top)) return; setHeight( @@ -184,14 +184,14 @@ function useFillHeight(element: HTMLElement | null): number | undefined { ); }, [element]); useEffect(() => { - if (!element || typeof window === "undefined") return; + if (!element || typeof window === 'undefined') return; fit(); - window.addEventListener("resize", fit); + window.addEventListener('resize', fit); const observer = - typeof IntersectionObserver === "undefined" ? undefined : new IntersectionObserver(fit); + typeof IntersectionObserver === 'undefined' ? undefined : new IntersectionObserver(fit); observer?.observe(element); return () => { - window.removeEventListener("resize", fit); + window.removeEventListener('resize', fit); observer?.disconnect(); }; }, [element, fit]); @@ -209,7 +209,7 @@ function useSize(): [(element: HTMLElement | null) => void, { width: number; hei // (which a browser may delay, e.g. in a background tab). const initial = element.getBoundingClientRect(); setSize({ width: Math.floor(initial.width), height: Math.floor(initial.height) }); - if (typeof ResizeObserver === "undefined") return; + if (typeof ResizeObserver === 'undefined') return; const observer = new ResizeObserver(entries => { const rect = entries[0].contentRect; setSize({ width: Math.floor(rect.width), height: Math.floor(rect.height) }); @@ -233,12 +233,12 @@ export default function EStepperView({ steps, profile, error, cseDiagram, layout const [hoveredFrame, setHoveredFrame] = useState(null); const [programHeight, setProgramHeight] = useRemembered( layout, - "programHeight", + 'programHeight', DEFAULT_PROGRAM_HEIGHT, ); const [programShare, setProgramShare] = useRemembered( layout, - "programShare", + 'programShare', DEFAULT_PROGRAM_SHARE, ); const [outputOpen, setOutputOpen] = useState(true); @@ -281,7 +281,7 @@ export default function EStepperView({ steps, profile, error, cseDiagram, layout const step = hasRun ? steps[stepIndex] : undefined; // The arrangement of the panes: side by side ("wide", the default) or one above the other // ("vertical"), as the user chooses, whatever the tab's width. - const [vertical, setVertical] = useRemembered(layout, "vertical", false); + const [vertical, setVertical] = useRemembered(layout, 'vertical', false); const wide = !vertical; // The double arrows (and `a` / `e`) jump to the previous / next breakpoint, or to the first / last @@ -324,8 +324,8 @@ export default function EStepperView({ steps, profile, error, cseDiagram, layout // The whole block is hoverable; in nested blocks, the innermost one under the mouse wins // (`mouseover` bubbles from it, and stops there). { event.stopPropagation(); setHoveredFrame(envId); @@ -352,12 +352,12 @@ export default function EStepperView({ steps, profile, error, cseDiagram, layout // the value a variable holds now (two calls of `make_withdraw` give two `withdraw`s). The // definition is in the tooltip. const title = - object?.kind === "function" - ? `function ${object.name ?? "lambda"}(${object.params.join(", ")})` + object?.kind === 'function' + ? `function ${object.name ?? 'lambda'}(${object.params.join(', ')})` : undefined; return ( setHovered(id)} @@ -389,11 +389,11 @@ export default function EStepperView({ steps, profile, error, cseDiagram, layout ), ); const onUp = () => { - window.removeEventListener("pointermove", onMove); - window.removeEventListener("pointerup", onUp); + window.removeEventListener('pointermove', onMove); + window.removeEventListener('pointerup', onUp); }; - window.addEventListener("pointermove", onMove); - window.addEventListener("pointerup", onUp); + window.addEventListener('pointermove', onMove); + window.addEventListener('pointerup', onUp); }, [stackedProgramHeight, maxProgramHeight], ); @@ -433,11 +433,11 @@ export default function EStepperView({ steps, profile, error, cseDiagram, layout setProgramShare(width / paneSpace); }; const onUp = () => { - window.removeEventListener("pointermove", onMove); - window.removeEventListener("pointerup", onUp); + window.removeEventListener('pointermove', onMove); + window.removeEventListener('pointerup', onUp); }; - window.addEventListener("pointermove", onMove); - window.addEventListener("pointerup", onUp); + window.addEventListener('pointermove', onMove); + window.addEventListener('pointerup', onUp); }, [paneSpace, programWidth], ); @@ -455,14 +455,14 @@ export default function EStepperView({ steps, profile, error, cseDiagram, layout ); const usingHostDiagram = cseDiagram !== undefined && cseSnapshots !== null; - const explanation = step?.markers?.[0]?.explanation ?? "..."; - const output = step?.output ?? ""; + const explanation = step?.markers?.[0]?.explanation ?? '...'; + const output = step?.output ?? ''; return (
@@ -483,7 +483,7 @@ export default function EStepperView({ steps, profile, error, cseDiagram, layout onChange={value => setStepValue(value + 1)} value={Math.min(stepValue, lastStep) - 1} /> -
+
{usingHostDiagram ? null : ( @@ -552,10 +552,10 @@ export default function EStepperView({ steps, profile, error, cseDiagram, layout ) ) : ( <> - +
{explanation}
-
+
setOutputOpen(o => !o)}> - {outputOpen ? "â–¾" : "â–¸"} Output + {outputOpen ? 'â–¾' : 'â–¸'} Output
{outputOpen ?
{output}
: null}
diff --git a/src/web/e-stepper/src/EnvDiagram.tsx b/src/web/e-stepper/src/EnvDiagram.tsx index 9cc7e5b..d4abf9c 100644 --- a/src/web/e-stepper/src/EnvDiagram.tsx +++ b/src/web/e-stepper/src/EnvDiagram.tsx @@ -1,14 +1,14 @@ -import type { CseDiagramAnchorResolver } from "@sourceacademy/common-cse-machine"; +import type { CseDiagramAnchorResolver } from '@sourceacademy/common-cse-machine'; import type { EStepperFrame, EStepperHeapObject, EStepperLookup, EStepperValue, -} from "@sourceacademy/common-e-stepper"; -import { useEffect, useMemo, useState } from "react"; -import { Arrow, Circle, Group, Layer, Line, Rect, Stage, Text } from "react-konva"; +} from '@sourceacademy/common-e-stepper'; +import { useEffect, useMemo, useState } from 'react'; +import { Arrow, Circle, Group, Layer, Line, Rect, Stage, Text } from 'react-konva'; -import { DiagramColors, frameColor } from "./colors"; +import { DiagramColors, frameColor } from './colors'; import { LayoutConfig as C, layoutDiagram, @@ -16,9 +16,9 @@ import { type ArrowSpec, type FrameBox, type ObjectBox, -} from "./layout"; +} from './layout'; -const FONT = "Inconsolata, Consolas, monospace"; +const FONT = 'Inconsolata, Consolas, monospace'; const FONT_SIZE = 13; interface Props { @@ -67,12 +67,12 @@ export default function EnvDiagram(props: Props) { // Arrows end at the top, horizontally centred, of an object (a function's circles, a // list's boxes), and at the left edge of a frame's box. const at = - kind === "object" + kind === 'object' ? layout.objects.find(b => b.object.id === id) : layout.frames.find(b => b.frame.id === id); if (!at) return null; const point = - "object" in at + 'object' in at ? { x: at.x + at.width / 2, y: at.y } : { x: at.x, y: at.y + C.headerHeight + at.height / 2 }; return { @@ -160,7 +160,7 @@ function FrameDrawing(props: { - {row.value.kind === "ref" ? ( + {row.value.kind === 'ref' ? ( )} @@ -244,7 +244,7 @@ function ObjectDrawing(props: { const strokeWidth = 2; const fill = props.hovered ? DiagramColors.hoverBackground : undefined; const label = - object.kind === "function" && object.name ? `${object.id} ${object.name}` : object.id; + object.kind === 'function' && object.name ? `${object.id} ${object.name}` : object.id; return ( - {object.kind === "function" ? ( + {object.kind === 'function' ? ( <> - element.kind === "ref" ? ( + element.kind === 'ref' ? ( diff --git a/src/web/e-stepper/src/ProgramArrows.tsx b/src/web/e-stepper/src/ProgramArrows.tsx index 3987e01..ab56d54 100644 --- a/src/web/e-stepper/src/ProgramArrows.tsx +++ b/src/web/e-stepper/src/ProgramArrows.tsx @@ -1,10 +1,10 @@ -import type { CseDiagramAnchorResolver, CseDiagramPoint } from "@sourceacademy/common-cse-machine"; -import { useLayoutEffect, useRef, useState } from "react"; +import type { CseDiagramAnchorResolver, CseDiagramPoint } from '@sourceacademy/common-cse-machine'; +import { useLayoutEffect, useRef, useState } from 'react'; /** An arrow from a badge or bracket in the program pane to an object or frame in the diagram. */ export interface ProgramArrow { key: string; - kind: "object" | "frame"; + kind: 'object' | 'frame'; /** The object's or frame's id. */ id: string; /** In the overlay's coordinates (pixels from the top-left of the panes' container). */ @@ -15,8 +15,8 @@ export interface ProgramArrow { } /** The marks in the program pane that arrows start from. */ -export const REF_ATTRIBUTE = "data-estepper-ref"; -export const ENV_ATTRIBUTE = "data-estepper-env"; +export const REF_ATTRIBUTE = 'data-estepper-ref'; +export const ENV_ATTRIBUTE = 'data-estepper-env'; const HEAD_LENGTH = 8; const HEAD_HALF_WIDTH = 4; @@ -25,7 +25,7 @@ const fmt = (p: CseDiagramPoint) => `${round(p.x)} ${round(p.y)}`; /** How an arrow arrives at its target: along the axis it travelled, down onto the top of an * object, or from the left into the left edge of a frame. */ -export type Arrival = "axis" | "above" | "left"; +export type Arrival = 'axis' | 'above' | 'left'; /** * The curve from `from` to `to` and its arrowhead. The curve leaves along the axis the two points @@ -37,7 +37,7 @@ export type Arrival = "axis" | "above" | "left"; export function arrowGeometry( from: CseDiagramPoint, to: CseDiagramPoint, - arrival: Arrival = "axis", + arrival: Arrival = 'axis', scale = 1, ): { path: string; head: string } { const dx = to.x - from.x; @@ -49,9 +49,9 @@ export function arrowGeometry( const lead = dx > 0 ? Math.max(24, Math.min(dx / 2, 120)) : 40; const c1 = horizontal ? { x: from.x + reach, y: from.y } : { x: from.x, y: from.y + reach }; const c2 = - arrival === "above" + arrival === 'above' ? { x: to.x, y: to.y - drop } - : arrival === "left" + : arrival === 'left' ? { x: to.x - lead, y: to.y } : horizontal ? { x: to.x - reach, y: to.y } @@ -95,7 +95,7 @@ export function computeProgramArrows( const programRect = program.getBoundingClientRect(); const diagramRect = diagram.getBoundingClientRect(); const arrows: ProgramArrow[] = []; - const collect = (attribute: string, kind: ProgramArrow["kind"]) => { + const collect = (attribute: string, kind: ProgramArrow['kind']) => { program.querySelectorAll(`[${attribute}]`).forEach((mark, index) => { const id = mark.getAttribute(attribute)!; const rect = mark.getBoundingClientRect(); @@ -116,8 +116,8 @@ export function computeProgramArrows( }); }); }; - collect(REF_ATTRIBUTE, "object"); - collect(ENV_ATTRIBUTE, "frame"); + collect(REF_ATTRIBUTE, 'object'); + collect(ENV_ATTRIBUTE, 'frame'); return arrows; } @@ -135,7 +135,7 @@ interface Props { colorOfFrame: (frameId: string) => string; } -const REF_COLOR = "#c5cbd3"; +const REF_COLOR = '#c5cbd3'; /** * Draws the arrows from the program pane into the diagram, over both: the arrows to objects all the @@ -156,8 +156,8 @@ export default function ProgramArrows({ useLayoutEffect(() => { const container = svgRef.current?.parentElement; - const program = container?.querySelector(".estepper-program"); - const diagram = container?.querySelector(".estepper-diagram"); + const program = container?.querySelector('.estepper-program'); + const diagram = container?.querySelector('.estepper-diagram'); const next = resolve && container && program && diagram ? computeProgramArrows(container, program, diagram, resolve) @@ -166,14 +166,14 @@ export default function ProgramArrows({ }); useLayoutEffect(() => { - if (typeof window === "undefined") return; - const program = svgRef.current?.parentElement?.querySelector(".estepper-program"); + if (typeof window === 'undefined') return; + const program = svgRef.current?.parentElement?.querySelector('.estepper-program'); const remeasure = () => setScrolled(n => n + 1); - program?.addEventListener("scroll", remeasure); - window.addEventListener("resize", remeasure); + program?.addEventListener('scroll', remeasure); + window.addEventListener('resize', remeasure); return () => { - program?.removeEventListener("scroll", remeasure); - window.removeEventListener("resize", remeasure); + program?.removeEventListener('scroll', remeasure); + window.removeEventListener('resize', remeasure); }; }, []); @@ -181,15 +181,15 @@ export default function ProgramArrows({ {arrows.map(arrow => { const emphasized = - arrow.kind === "object" ? hovered === arrow.id : hoveredFrame === arrow.id; - if (!emphasized && (arrow.kind === "frame" || !all)) return null; + arrow.kind === 'object' ? hovered === arrow.id : hoveredFrame === arrow.id; + if (!emphasized && (arrow.kind === 'frame' || !all)) return null; const { path, head } = arrowGeometry( arrow.from, arrow.to, - arrow.kind === "object" ? "above" : "left", + arrow.kind === 'object' ? 'above' : 'left', arrow.scale, ); - const color = arrow.kind === "frame" ? colorOfFrame(arrow.id) : REF_COLOR; + const color = arrow.kind === 'frame' ? colorOfFrame(arrow.id) : REF_COLOR; return ( ({ default: {} as any })); vi.mock(import('react-konva'), () => { const stub = (name: string) => (props: Record) => createElement(`konva-${name.toLowerCase()}`, props, props.children as never); return Object.fromEntries( - ["Stage", "Layer", "Text", "Group", "Line", "Rect", "Circle", "Arrow"].map(n => [n, stub(n)]), + ['Stage', 'Layer', 'Text', 'Group', 'Line', 'Rect', 'Circle', 'Arrow'].map(n => [n, stub(n)]), ); }); // Blueprint's overlays need a DOM to unmount (the options menu goes when the host draws the diagram). vi.mock(import('@blueprintjs/core'), async importOriginal => ({ ...(await importOriginal()), - Popover: ((props: Record) => createElement("popover-stub", props)) as any + Popover: ((props: Record) => createElement('popover-stub', props)) as any, })); type PluginArgs = ConstructorParameters; @@ -27,7 +27,7 @@ function setUp(hostServices?: PluginArgs[3]) { let deliver!: (message: EStepperMessage) => void; const sent: EStepperMessage[] = []; const channel = { - name: "__e_stepper", + name: '__e_stepper', subscribe: (fn: (message: EStepperMessage) => void) => (deliver = fn), unsubscribe: () => {}, send: (message: EStepperMessage) => sent.push(message), @@ -45,21 +45,21 @@ function setUp(hostServices?: PluginArgs[3]) { describe(EStepperHostPlugin, () => { test("registers and reveals the E-Stepper tab, and asks for the last run's steps", () => { const { tab, tabService, sent } = setUp(); - expect(tab).toMatchObject({ id: "e-stepper", label: "E-Stepper", iconName: "flow-review" }); - expect(tabService.revealTab).toHaveBeenCalledWith("e-stepper"); - expect(sent).toEqual([{ type: "request" }]); + expect(tab).toMatchObject({ id: 'e-stepper', label: 'E-Stepper', iconName: 'flow-review' }); + expect(tabService.revealTab).toHaveBeenCalledWith('e-stepper'); + expect(sent).toEqual([{ type: 'request' }]); }); - test("keeps the latest steps or error", () => { + test('keeps the latest steps or error', () => { const { plugin, deliver } = setUp(); - deliver({ type: "steps", steps: steps as unknown as EStepperStep[] }); + deliver({ type: 'steps', steps: steps as unknown as EStepperStep[] }); expect(plugin.getState().steps).toHaveLength(3); - deliver({ type: "error", error: "SyntaxError" }); - expect(plugin.getState()).toMatchObject({ steps: [], error: "SyntaxError" }); + deliver({ type: 'error', error: 'SyntaxError' }); + expect(plugin.getState()).toMatchObject({ steps: [], error: 'SyntaxError' }); }); test("lends the host's CSE diagram to the tab", () => { - const createView = vi.fn(() => createElement("host-cse-view")); + const createView = vi.fn(() => createElement('host-cse-view')); const { tab, deliver } = setUp({ cseDiagram: { createView } }); let view!: TestRenderer.ReactTestRenderer; act(() => { @@ -68,14 +68,14 @@ describe(EStepperHostPlugin, () => { }); }); deliver({ - type: "steps", + type: 'steps', steps: (steps as unknown as EStepperStep[]).map((s, i) => ({ ...s, cse: { stepIndex: i, control: [], stash: [], environments: [] }, })), }); expect(createView).toHaveBeenCalled(); - expect(view.root.findAllByType("host-cse-view" as unknown as React.ElementType)).toHaveLength( + expect(view.root.findAllByType('host-cse-view' as unknown as React.ElementType)).toHaveLength( 1, ); }); diff --git a/src/web/e-stepper/src/__tests__/EStepperView.test.tsx b/src/web/e-stepper/src/__tests__/EStepperView.test.tsx index f4cbd8a..99538ff 100644 --- a/src/web/e-stepper/src/__tests__/EStepperView.test.tsx +++ b/src/web/e-stepper/src/__tests__/EStepperView.test.tsx @@ -1,14 +1,14 @@ -import { Button, Checkbox, Popover, Slider, Switch } from "@blueprintjs/core"; -import type { CseDiagramViewProps } from "@sourceacademy/common-cse-machine"; -import type { EStepperStep, SyntaxProfile } from "@sourceacademy/common-e-stepper"; -import { act, createElement } from "react"; -import TestRenderer from "react-test-renderer"; -import { beforeEach, describe, expect, test, vi } from "vitest"; - -import EStepperView, { nextBreakpoint, previousBreakpoint, sliderLabels } from "../EStepperView"; -import ProgramArrows from "../ProgramArrows"; -import steps from "./makeWithdrawSteps.json"; -import pythonProfile from "./pythonProfile.json"; +import { Button, Checkbox, Popover, Slider, Switch } from '@blueprintjs/core'; +import type { CseDiagramViewProps } from '@sourceacademy/common-cse-machine'; +import type { EStepperStep, SyntaxProfile } from '@sourceacademy/common-e-stepper'; +import { act, createElement } from 'react'; +import TestRenderer from 'react-test-renderer'; +import { beforeEach, describe, expect, test, vi } from 'vitest'; + +import EStepperView, { nextBreakpoint, previousBreakpoint, sliderLabels } from '../EStepperView'; +import ProgramArrows from '../ProgramArrows'; +import steps from './makeWithdrawSteps.json'; +import pythonProfile from './pythonProfile.json'; // As in the data visualizer's tests: avoid Konva's Node build (it requires the optional `canvas`). vi.mock(import('konva'), () => ({ default: {} as any })); @@ -16,14 +16,14 @@ vi.mock(import('react-konva'), () => { const stub = (name: string) => (props: Record) => createElement(`konva-${name.toLowerCase()}`, props, props.children as never); return Object.fromEntries( - ["Stage", "Layer", "Text", "Group", "Line", "Rect", "Circle", "Arrow"].map(n => [n, stub(n)]), + ['Stage', 'Layer', 'Text', 'Group', 'Line', 'Rect', 'Circle', 'Arrow'].map(n => [n, stub(n)]), ); }); // Blueprint's overlays need a DOM; the tests read the options menu's content from the props. vi.mock(import('@blueprintjs/core'), async (importOriginal: () => Promise) => ({ ...(await importOriginal()), - Popover: ((props: Record) => createElement("popover-stub", props)) as any + Popover: ((props: Record) => createElement('popover-stub', props)) as any, })); /** A stubbed Konva shape's host element type (see the react-konva mock above). */ @@ -37,24 +37,24 @@ const profile = pythonProfile as unknown as SyntaxProfile; const withRefs: EStepperStep = { ...fixture[2], ast: { - type: "Program", - nodeId: "p", + type: 'Program', + nodeId: 'p', body: [ { - type: "ExpressionStatement", - nodeId: "s", + type: 'ExpressionStatement', + nodeId: 's', expression: { - type: "CallExpression", - nodeId: "c", + type: 'CallExpression', + nodeId: 'c', callee: { - type: "Builtin", - nodeId: "f", - name: "print", - hoverText: "built-in function print", + type: 'Builtin', + nodeId: 'f', + name: 'print', + hoverText: 'built-in function print', }, arguments: [ - { type: "Ref", nodeId: "r1", objectId: "#2" }, - { type: "Ref", nodeId: "r2", objectId: "#4" }, + { type: 'Ref', nodeId: 'r1', objectId: '#2' }, + { type: 'Ref', nodeId: 'r2', objectId: '#4' }, ], }, }, @@ -75,35 +75,35 @@ function render(props: Parameters[0], width = 600) { /** Whether a node has the CSS class `name` (among its space-separated classes). */ const hasClass = (node: TestRenderer.ReactTestInstance, name: string): boolean => - typeof node.props.className === "string" && node.props.className.split(" ").includes(name); + typeof node.props.className === 'string' && node.props.className.split(' ').includes(name); const text = (node: TestRenderer.ReactTestInstance): string => - node.children.map(c => (typeof c === "string" ? c : text(c))).join(""); + node.children.map(c => (typeof c === 'string' ? c : text(c))).join(''); describe(EStepperView, () => { // The slider remounts when the tab's size is known (it measures its track then), and a Blueprint // slider takes its listeners off the document when it goes away. beforeEach(() => { - vi.stubGlobal("document", { + vi.stubGlobal('document', { addEventListener() {}, removeEventListener() {}, - getElementById: () => ({ textContent: "" }), + getElementById: () => ({ textContent: '' }), }); }); - test("shows the welcome text before anything has run", () => { + test('shows the welcome text before anything has run', () => { const view = render({ steps: [] }); - expect(text(view.root)).toContain("Welcome to the environment stepper"); + expect(text(view.root)).toContain('Welcome to the environment stepper'); }); - test("the welcome text links to the guide for students", () => { + test('the welcome text links to the guide for students', () => { const view = render({ steps: [] }); - const link = view.root.findByType("a"); - expect(link.props.href).toBe("https://docs.sourceacademy.org/python/e-stepper/"); - expect(link.props.target).toBe("_blank"); + const link = view.root.findByType('a'); + expect(link.props.href).toBe('https://docs.sourceacademy.org/python/e-stepper/'); + expect(link.props.target).toBe('_blank'); }); - test("the slider counts the steps taken: 0 on the left, the total on the right", () => { + test('the slider counts the steps taken: 0 on the left, the total on the right', () => { const run = Array.from({ length: 174 }, (_, i) => ({ ...fixture[0], key: i })); const view = render({ steps: run, profile }); const slider = () => view.root.findByType(Slider); @@ -118,13 +118,13 @@ describe(EStepperView, () => { expect(slider().props.value).toBe(173); act(() => view.root - .find(n => hasClass(n, "sa-e-stepper")) - .props.onKeyDown({ key: "a", preventDefault: () => {} }), + .find(n => hasClass(n, 'sa-e-stepper')) + .props.onKeyDown({ key: 'a', preventDefault: () => {} }), ); expect(slider().props.value).toBe(0); }); - test("a run of one step gives the slider a valid range, disabled", () => { + test('a run of one step gives the slider a valid range, disabled', () => { const view = render({ steps: [fixture[0]], profile }); const slider = view.root.findByType(Slider); expect(slider.props.min).toBe(0); @@ -134,7 +134,7 @@ describe(EStepperView, () => { expect(render({ steps: fixture, profile }).root.findByType(Slider).props.disabled).toBe(false); }); - test("labels the ends of the slider and a round step in between, never crowding the end", () => { + test('labels the ends of the slider and a round step in between, never crowding the end', () => { expect(sliderLabels(0)).toEqual([0]); expect(sliderLabels(1)).toEqual([0, 1]); expect(sliderLabels(10)).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); @@ -147,13 +147,13 @@ describe(EStepperView, () => { expect(sliderLabels(37)).toEqual([0, 4, 8, 12, 16, 20, 24, 28, 32, 37]); }); - describe("breakpoints", () => { + describe('breakpoints', () => { /** Ten steps; steps 4 and 8 (1-based) are breakpoints. */ - const stop: EStepperStep["markers"] = [ + const stop: EStepperStep['markers'] = [ { - explanation: "Running breakpoint()", - redexType: "beforeMarker", - redexNodeType: "DebuggerStatement", + explanation: 'Running breakpoint()', + redexType: 'beforeMarker', + redexNodeType: 'DebuggerStatement', }, ]; const run = Array.from({ length: 10 }, (_, i) => ({ @@ -161,53 +161,53 @@ describe(EStepperView, () => { markers: i === 3 || i === 7 ? stop : [{ explanation: `step ${i + 1}` }], })); - test("the next breakpoint after a step, or else the last step", () => { + test('the next breakpoint after a step, or else the last step', () => { expect([1, 3].map(v => nextBreakpoint(run, v))).toEqual([4, 4]); expect([4, 7].map(v => nextBreakpoint(run, v))).toEqual([8, 8]); expect([8, 9, 10].map(v => nextBreakpoint(run, v))).toEqual([10, 10, 10]); }); - test("the previous breakpoint before a step, or else the first step", () => { + test('the previous breakpoint before a step, or else the first step', () => { expect([10, 9, 8].map(v => previousBreakpoint(run, v))).toEqual([8, 8, 4]); expect([5, 4].map(v => previousBreakpoint(run, v))).toEqual([4, 1]); expect(previousBreakpoint(run, 1)).toBe(1); }); - test("without breakpoints they are the first and last step", () => { - const plain = run.map(s => ({ ...s, markers: [{ explanation: "x" }] })); + test('without breakpoints they are the first and last step', () => { + const plain = run.map(s => ({ ...s, markers: [{ explanation: 'x' }] })); expect(nextBreakpoint(plain, 3)).toBe(10); expect(previousBreakpoint(plain, 7)).toBe(1); expect(nextBreakpoint([], 1)).toBe(1); }); - test("the double arrows and a / e jump between them", () => { + test('the double arrows and a / e jump between them', () => { const view = render({ steps: run, profile }); const slider = () => view.root.findByType(Slider).props.value as number; const press = (key: string) => act(() => view.root - .find(n => hasClass(n, "sa-e-stepper")) + .find(n => hasClass(n, 'sa-e-stepper')) .props.onKeyDown({ key, preventDefault: () => {} }), ); const arrow = (icon: string) => view.root.findAllByType(Button).find(b => b.props.icon === icon)!; expect(slider()).toBe(0); - press("e"); + press('e'); expect(slider()).toBe(3); // step 4, the first breakpoint (the slider counts from 0) - act(() => arrow("double-chevron-right").props.onClick()); + act(() => arrow('double-chevron-right').props.onClick()); expect(slider()).toBe(7); - press("e"); + press('e'); expect(slider()).toBe(9); // no more: the last step - press("a"); + press('a'); expect(slider()).toBe(7); - act(() => arrow("double-chevron-left").props.onClick()); + act(() => arrow('double-chevron-left').props.onClick()); expect(slider()).toBe(3); - press("a"); + press('a'); expect(slider()).toBe(0); }); }); - test("has no step controls before anything has run, and has them once there are steps", () => { + test('has no step controls before anything has run, and has them once there are steps', () => { const before = render({ steps: [] }); expect(before.root.findAllByType(Slider)).toHaveLength(0); expect(before.root.findAllByType(Button)).toHaveLength(0); @@ -216,76 +216,76 @@ describe(EStepperView, () => { expect(after.root.findAllByType(Button).length).toBeGreaterThan(0); }); - test("shows a runner error", () => { + test('shows a runner error', () => { const view = render({ steps: [], error: "NameError: name 'x' is not defined" }); expect(text(view.root)).toContain("NameError: name 'x' is not defined"); }); - test("renders a function body under evaluation as a bracket labelled with its frame", () => { + test('renders a function body under evaluation as a bracket labelled with its frame', () => { const view = render({ steps: [fixture[1]], profile }); - const blocks = view.root.findAll(n => n.props.className === "estepper-envblock"); + const blocks = view.root.findAll(n => n.props.className === 'estepper-envblock'); expect(blocks).toHaveLength(1); - const label = blocks[0].find(n => n.props.className === "estepper-envblock-label"); - expect(text(label)).toBe("E2"); + const label = blocks[0].find(n => n.props.className === 'estepper-envblock-label'); + expect(text(label)).toBe('E2'); }); test("renders references as badges only; a function object's name is in its tooltip", () => { const view = render({ steps: [withRefs], profile }); - expect(view.root.findAll(n => hasClass(n, "estepper-ref"))).toHaveLength(2); - const badges = view.root.findAll(n => n.props.className === "estepper-ref-badge").map(text); - expect(badges).toEqual(["#2", "#4"]); - expect(view.root.findAll(n => n.props.className === "estepper-ref-name")).toHaveLength(0); + expect(view.root.findAll(n => hasClass(n, 'estepper-ref'))).toHaveLength(2); + const badges = view.root.findAll(n => n.props.className === 'estepper-ref-badge').map(text); + expect(badges).toEqual(['#2', '#4']); + expect(view.root.findAll(n => n.props.className === 'estepper-ref-name')).toHaveLength(0); const titles = view.root - .findAll(n => hasClass(n, "estepper-ref")) + .findAll(n => hasClass(n, 'estepper-ref')) .map(n => n.props.title as string | undefined); - expect(titles[0]).toBe("function withdraw(amount)"); + expect(titles[0]).toBe('function withdraw(amount)'); }); - test("hovering a reference highlights it", () => { + test('hovering a reference highlights it', () => { const view = render({ steps: [withRefs], profile }); - const [first] = view.root.findAll(n => hasClass(n, "estepper-ref")); + const [first] = view.root.findAll(n => hasClass(n, 'estepper-ref')); act(() => first.props.onMouseEnter()); - const hovered = view.root.findAll(n => hasClass(n, "estepper-ref") && hasClass(n, "hovered")); + const hovered = view.root.findAll(n => hasClass(n, 'estepper-ref') && hasClass(n, 'hovered')); expect(hovered).toHaveLength(1); - expect(text(hovered[0])).toContain("#2"); + expect(text(hovered[0])).toContain('#2'); act(() => hovered[0].props.onMouseLeave()); - expect(view.root.findAll(n => hasClass(n, "hovered"))).toHaveLength(0); + expect(view.root.findAll(n => hasClass(n, 'hovered'))).toHaveLength(0); }); test("shows the step's explanation and the program's output", () => { const view = render({ steps: [fixture[2]], profile }); const all = text(view.root); - expect(all).toContain("Evaluation complete"); - expect(all).toContain("Output"); - expect(all).toContain("50"); + expect(all).toContain('Evaluation complete'); + expect(all).toContain('Output'); + expect(all).toContain('50'); }); - test("draws the environment diagram for the current step", () => { + test('draws the environment diagram for the current step', () => { const view = render({ steps: [fixture[1]], profile }); - expect(view.root.findAllByType(konva("konva-stage"))).toHaveLength(1); + expect(view.root.findAllByType(konva('konva-stage'))).toHaveLength(1); }); - test("stacks the panes when Vertical is chosen, with a divider that resizes the program pane", () => { + test('stacks the panes when Vertical is chosen, with a divider that resizes the program pane', () => { const view = render({ steps: [fixture[1]], profile }); act(() => view.root .findAllByType(Button) - .find(b => b.props["aria-label"] === "Vertical")! + .find(b => b.props['aria-label'] === 'Vertical')! .props.onClick(), ); - const main = view.root.find(n => hasClass(n, "estepper-main")); - expect(hasClass(main, "narrow")).toBe(true); + const main = view.root.find(n => hasClass(n, 'estepper-main')); + expect(hasClass(main, 'narrow')).toBe(true); const listeners: Record void> = {}; - vi.stubGlobal("window", { + vi.stubGlobal('window', { addEventListener: (type: string, fn: (e: { clientY: number }) => void) => (listeners[type] = fn), removeEventListener: (type: string) => delete listeners[type], }); - const divider = view.root.find(n => hasClass(n, "estepper-divider")); + const divider = view.root.find(n => hasClass(n, 'estepper-divider')); act(() => divider.props.onPointerDown({ clientY: 100 })); act(() => listeners.pointermove({ clientY: 160 })); - const left = view.root.find(n => hasClass(n, "estepper-left")); - expect(left.props.style.flex).toBe("0 0 300px"); + const left = view.root.find(n => hasClass(n, 'estepper-left')); + expect(left.props.style.flex).toBe('0 0 300px'); act(() => listeners.pointerup({ clientY: 160 })); expect(listeners.pointermove).toBeUndefined(); vi.unstubAllGlobals(); @@ -297,52 +297,52 @@ describe(EStepperView, () => { act(() => first.root .findAllByType(Button) - .find(b => b.props["aria-label"] === "Vertical")! + .find(b => b.props['aria-label'] === 'Vertical')! .props.onClick(), ); act(() => first.unmount()); const second = render({ steps: [fixture[1]], profile, layout }); - const main = second.root.find(n => hasClass(n, "estepper-main")); - expect(hasClass(main, "narrow")).toBe(true); + const main = second.root.find(n => hasClass(n, 'estepper-main')); + expect(hasClass(main, 'narrow')).toBe(true); }); - test("the divider can not be dragged out of view: the diagram below keeps its least height", () => { + test('the divider can not be dragged out of view: the diagram below keeps its least height', () => { // The panes' container is 600px high (see `render`'s node mock): 80px stay for the diagram, // and 26px for the divider and its gaps. const view = render({ steps: [fixture[1]], profile }); act(() => view.root .findAllByType(Button) - .find(b => b.props["aria-label"] === "Vertical")! + .find(b => b.props['aria-label'] === 'Vertical')! .props.onClick(), ); const listeners: Record void> = {}; - vi.stubGlobal("window", { + vi.stubGlobal('window', { addEventListener: (type: string, fn: (e: { clientY: number }) => void) => (listeners[type] = fn), removeEventListener: (type: string) => delete listeners[type], }); - const divider = view.root.find(n => hasClass(n, "estepper-divider")); + const divider = view.root.find(n => hasClass(n, 'estepper-divider')); act(() => divider.props.onPointerDown({ clientY: 0 })); act(() => listeners.pointermove({ clientY: 100000 })); - const left = view.root.find(n => hasClass(n, "estepper-left")); - expect(left.props.style.flex).toBe("0 0 494px"); + const left = view.root.find(n => hasClass(n, 'estepper-left')); + expect(left.props.style.flex).toBe('0 0 494px'); act(() => listeners.pointerup({ clientY: 100000 })); // And back: the divider is still there to be dragged. act(() => - view.root.find(n => hasClass(n, "estepper-divider")).props.onPointerDown({ clientY: 0 }), + view.root.find(n => hasClass(n, 'estepper-divider')).props.onPointerDown({ clientY: 0 }), ); act(() => listeners.pointermove({ clientY: -100000 })); // The program may shrink to about one line. - expect(view.root.find(n => hasClass(n, "estepper-left")).props.style.flex).toBe("0 0 40px"); + expect(view.root.find(n => hasClass(n, 'estepper-left')).props.style.flex).toBe('0 0 40px'); vi.unstubAllGlobals(); }); - test("the slider measures its track again when the tab is resized (a click maps to the right step)", () => { + test('the slider measures its track again when the tab is resized (a click maps to the right step)', () => { const observers: ((entries: { contentRect: { width: number; height: number } }[]) => void)[] = []; vi.stubGlobal( - "ResizeObserver", + 'ResizeObserver', class { constructor(callback: (typeof observers)[number]) { observers.push(callback); @@ -364,7 +364,7 @@ describe(EStepperView, () => { const observers: ((entries: { contentRect: { width: number; height: number } }[]) => void)[] = []; vi.stubGlobal( - "ResizeObserver", + 'ResizeObserver', class { constructor(callback: (typeof observers)[number]) { observers.push(callback); @@ -376,11 +376,11 @@ describe(EStepperView, () => { const view = render({ steps: [fixture[1]], profile }, 1200); const narrow = () => hasClass( - view.root.find(n => hasClass(n, "estepper-main")), - "narrow", + view.root.find(n => hasClass(n, 'estepper-main')), + 'narrow', ); const vertical = () => - view.root.findAllByType(Button).find(b => b.props["aria-label"] === "Vertical")!; + view.root.findAllByType(Button).find(b => b.props['aria-label'] === 'Vertical')!; const resize = (width: number) => act(() => observers.forEach(cb => cb([{ contentRect: { width, height: 600 } }]))); // Wide at first: side by side, the box not ticked. @@ -398,18 +398,18 @@ describe(EStepperView, () => { act(() => vertical().props.onClick()); expect(narrow()).toBe(false); // The button states its state; the checkbox in it is only a picture. - expect(vertical().props["aria-pressed"]).toBe(false); + expect(vertical().props['aria-pressed']).toBe(false); act(() => vertical().props.onClick()); - expect(vertical().props["aria-pressed"]).toBe(true); + expect(vertical().props['aria-pressed']).toBe(true); expect(view.root.findByType(Checkbox).props.tabIndex).toBe(-1); vi.unstubAllGlobals(); }); - test("side by side in a tab too narrow for both panes: the program keeps its least width, never negative", () => { + test('side by side in a tab too narrow for both panes: the program keeps its least width, never negative', () => { const observers: ((entries: { contentRect: { width: number; height: number } }[]) => void)[] = []; vi.stubGlobal( - "ResizeObserver", + 'ResizeObserver', class { constructor(callback: (typeof observers)[number]) { observers.push(callback); @@ -419,7 +419,7 @@ describe(EStepperView, () => { }, ); const view = render({ steps: [fixture[1]], profile }, 1200); - const left = () => view.root.find(n => hasClass(n, "estepper-left")).props.style.flex; + const left = () => view.root.find(n => hasClass(n, 'estepper-left')).props.style.flex; for (const width of [250, 150, 10]) { act(() => observers.forEach(cb => cb([{ contentRect: { width, height: 600 } }]))); expect(left()).toBe(`0 0 ${Math.min(44, Math.max(0, width - 18))}px`); @@ -427,14 +427,14 @@ describe(EStepperView, () => { vi.unstubAllGlobals(); }); - test("puts the panes side by side in a wide tab, with a divider that resizes the program pane", () => { + test('puts the panes side by side in a wide tab, with a divider that resizes the program pane', () => { // The tab's size, as the ResizeObservers report it (one for the tab, one for the diagram). const observers: ((entries: { contentRect: { width: number; height: number } }[]) => void)[] = []; const resize = (width: number) => observers.forEach(callback => callback([{ contentRect: { width, height: 600 } }])); vi.stubGlobal( - "ResizeObserver", + 'ResizeObserver', class { constructor(callback: (typeof observers)[number]) { observers.push(callback); @@ -444,25 +444,25 @@ describe(EStepperView, () => { }, ); const view = render({ steps: [fixture[1]], profile }, 1200); - const main = view.root.find(n => hasClass(n, "estepper-main")); - expect(hasClass(main, "narrow")).toBe(false); - const left = () => view.root.find(n => hasClass(n, "estepper-left")).props.style.flex; + const main = view.root.find(n => hasClass(n, 'estepper-main')); + expect(hasClass(main, 'narrow')).toBe(false); + const left = () => view.root.find(n => hasClass(n, 'estepper-left')).props.style.flex; // 1200px wide, of which 1182px are left for the panes (2 gaps of 8px, and 2px of divider). expect(left()).toBe(`0 0 ${Math.round(0.45 * 1182)}px`); const listeners: Record void> = {}; - vi.stubGlobal("window", { + vi.stubGlobal('window', { addEventListener: (type: string, fn: (e: { clientX: number }) => void) => (listeners[type] = fn), removeEventListener: (type: string) => delete listeners[type], }); - const divider = view.root.find(n => hasClass(n, "estepper-divider")); - expect(hasClass(divider, "vertical")).toBe(true); + const divider = view.root.find(n => hasClass(n, 'estepper-divider')); + expect(hasClass(divider, 'vertical')).toBe(true); act(() => divider.props.onPointerDown({ clientX: 600, preventDefault: () => {} })); act(() => listeners.pointermove({ clientX: 480 })); expect(left()).toBe(`0 0 ${Math.round(0.45 * 1182) - 120}px`); // The program keeps at least 44px (about three characters), the diagram 200px. act(() => listeners.pointermove({ clientX: 0 })); - expect(left()).toBe("0 0 44px"); + expect(left()).toBe('0 0 44px'); act(() => listeners.pointermove({ clientX: 2000 })); expect(left()).toBe(`0 0 ${1182 - 200}px`); act(() => listeners.pointerup({ clientX: 2000 })); @@ -473,7 +473,7 @@ describe(EStepperView, () => { vi.unstubAllGlobals(); }); - test("fills the height down to the bottom of the browser window", () => { + test('fills the height down to the bottom of the browser window', () => { let renderer!: TestRenderer.ReactTestRenderer; const element = ; act(() => { @@ -483,10 +483,10 @@ describe(EStepperView, () => { }), }); }); - const root = () => renderer.root.find(n => hasClass(n, "sa-e-stepper")); + const root = () => renderer.root.find(n => hasClass(n, 'sa-e-stepper')); // (The test environment has no window until stubbed: then the height is worked out again.) const browser = { innerHeight: 1000, addEventListener() {}, removeEventListener() {} }; - vi.stubGlobal("window", browser); + vi.stubGlobal('window', browser); act(() => renderer.update()); // From its top (200px) to the window's bottom (1000px), less a 16px gap... expect(root().props.style).toEqual({ height: 784 }); @@ -497,7 +497,7 @@ describe(EStepperView, () => { vi.unstubAllGlobals(); }); - test("leaves room for the margins and paddings of the containers around it", () => { + test('leaves room for the margins and paddings of the containers around it', () => { // As in the Source Academy frontend: a margin on the tab, and margins and paddings on the // panels around it, each of which would make a panel scroll if the tab went down to the // bottom of the window regardless. @@ -514,16 +514,16 @@ describe(EStepperView, () => { createNodeMock: () => element, }); }); - vi.stubGlobal("window", { innerHeight: 676, addEventListener() {}, removeEventListener() {} }); - vi.stubGlobal("document", { body: {} }); - vi.stubGlobal("getComputedStyle", (e: { m: number }) => ({ + vi.stubGlobal('window', { innerHeight: 676, addEventListener() {}, removeEventListener() {} }); + vi.stubGlobal('document', { body: {} }); + vi.stubGlobal('getComputedStyle', (e: { m: number }) => ({ marginBottom: `${e.m}px`, - paddingBottom: "0px", - borderBottomWidth: "0px", + paddingBottom: '0px', + borderBottomWidth: '0px', })); act(() => renderer.update()); // 676 - 164 - ceil(15 + 6.4 + 6.4 + 9.6) - expect(renderer.root.find(n => hasClass(n, "sa-e-stepper")).props.style).toEqual({ + expect(renderer.root.find(n => hasClass(n, 'sa-e-stepper')).props.style).toEqual({ height: 676 - 164 - 38, }); vi.unstubAllGlobals(); @@ -531,37 +531,37 @@ describe(EStepperView, () => { test("the explanation takes the height of its text, whatever the host's styles", () => { const view = render({ steps: [fixture[1]], profile }); - const card = view.root.findAll(n => hasClass(n, "bp6-card") || hasClass(n, "bp5-card"))[0]; - expect(card.props.style).toMatchObject({ height: "auto", flex: "0 0 auto" }); + const card = view.root.findAll(n => hasClass(n, 'bp6-card') || hasClass(n, 'bp5-card'))[0]; + expect(card.props.style).toMatchObject({ height: 'auto', flex: '0 0 auto' }); }); - test("steps with the buttons and the keyboard", () => { + test('steps with the buttons and the keyboard', () => { const view = render({ steps: fixture, profile }); const explanation = () => - text(view.root.findAll(n => n.props.className === "result-output")[0]); + text(view.root.findAll(n => n.props.className === 'result-output')[0]); const press = (key: string) => act(() => view.root - .find(n => hasClass(n, "sa-e-stepper")) + .find(n => hasClass(n, 'sa-e-stepper')) .props.onKeyDown({ key, preventDefault: () => {} }), ); - expect(explanation()).toBe("Start of evaluation"); - press("e"); - expect(explanation()).toBe("Evaluation complete"); - press("b"); - expect(explanation()).toBe("Assigned balance = 50 in frame E1"); - press("a"); - expect(explanation()).toBe("Start of evaluation"); - press("f"); - expect(explanation()).toBe("Assigned balance = 50 in frame E1"); + expect(explanation()).toBe('Start of evaluation'); + press('e'); + expect(explanation()).toBe('Evaluation complete'); + press('b'); + expect(explanation()).toBe('Assigned balance = 50 in frame E1'); + press('a'); + expect(explanation()).toBe('Start of evaluation'); + press('f'); + expect(explanation()).toBe('Assigned balance = 50 in frame E1'); const buttons = view.root.findAllByType(Button); act(() => buttons[3].props.onClick()); - expect(explanation()).toBe("Evaluation complete"); + expect(explanation()).toBe('Evaluation complete'); act(() => buttons[0].props.onClick()); - expect(explanation()).toBe("Start of evaluation"); + expect(explanation()).toBe('Start of evaluation'); }); - describe("display options", () => { + describe('display options', () => { /** The options menu's switches, by label; the menu's content is rendered on its own. */ function options(view: TestRenderer.ReactTestRenderer) { let menu!: TestRenderer.ReactTestRenderer; @@ -572,11 +572,11 @@ describe(EStepperView, () => { menu.root.findAllByType(Switch).map(sw => [sw.props.label as string, sw]), ); } - test("arrows are on by default; turning them off leaves the arrows of what is hovered", () => { + test('arrows are on by default; turning them off leaves the arrows of what is hovered', () => { const view = render({ steps: fixture, profile }); const diagram = view.root.find(n => n.props.onAnchors && n.props.frames); act(() => diagram.props.onAnchors(() => ({ x: 1, y: 2 }))); - const arrows = options(view)["From program"]; + const arrows = options(view)['From program']; expect(arrows.props.checked).toBe(true); expect(view.root.findAllByType(ProgramArrows)).toHaveLength(1); expect(view.root.findByType(ProgramArrows).props.all).toBe(true); @@ -585,7 +585,7 @@ describe(EStepperView, () => { expect(view.root.findByType(ProgramArrows).props.all).toBe(false); }); - test("Clear dead frames is also available when only a heap object is dead", () => { + test('Clear dead frames is also available when only a heap object is dead', () => { const onlyObject: EStepperStep = { ...fixture[2], frames: fixture[2].frames.map(f => ({ ...f, isGarbage: false })), @@ -594,21 +594,21 @@ describe(EStepperView, () => { const view = render({ steps: [onlyObject], profile }); const clear = view.root .findAllByType(Button) - .find(b => b.props.text === "Clear dead frames")!; + .find(b => b.props.text === 'Clear dead frames')!; expect(clear.props.disabled).toBe(false); }); - test("Clear dead frames clears them from the diagram until the step changes", () => { + test('Clear dead frames clears them from the diagram until the step changes', () => { const view = render({ steps: fixture, profile }); const diagram = () => view.root.find(n => n.props.onAnchors && n.props.frames); const clear = () => - view.root.findAllByType(Button).find(b => b.props.text === "Clear dead frames")!; + view.root.findAllByType(Button).find(b => b.props.text === 'Clear dead frames')!; // Only the last step has dead frames (E2). expect(clear().props.disabled).toBe(true); act(() => view.root - .find(n => hasClass(n, "sa-e-stepper")) - .props.onKeyDown({ key: "e", preventDefault: () => {} }), + .find(n => hasClass(n, 'sa-e-stepper')) + .props.onKeyDown({ key: 'e', preventDefault: () => {} }), ); expect(diagram().props.clearDead).toBe(false); expect(clear().props.disabled).toBe(false); @@ -617,18 +617,18 @@ describe(EStepperView, () => { expect(clear().props.disabled).toBe(true); act(() => view.root - .find(n => hasClass(n, "sa-e-stepper")) - .props.onKeyDown({ key: "b", preventDefault: () => {} }), + .find(n => hasClass(n, 'sa-e-stepper')) + .props.onKeyDown({ key: 'b', preventDefault: () => {} }), ); expect(diagram().props.clearDead).toBe(false); }); }); - test("the output strip can be collapsed", () => { + test('the output strip can be collapsed', () => { const view = render({ steps: [fixture[2]], profile }); - const toggle = view.root.find(n => hasClass(n, "estepper-output-toggle")); + const toggle = view.root.find(n => hasClass(n, 'estepper-output-toggle')); act(() => toggle.props.onClick()); - expect(view.root.findAll(n => hasClass(n, "estepper-output"))).toHaveLength(0); + expect(view.root.findAll(n => hasClass(n, 'estepper-output'))).toHaveLength(0); }); describe("with the host's CSE machine diagram", () => { @@ -638,7 +638,7 @@ describe(EStepperView, () => { })); const service = () => { const createView = vi.fn((props: CseDiagramViewProps) => - createElement("host-cse-view", { "data-step": props.step }), + createElement('host-cse-view', { 'data-step': props.step }), ); return { createView }; }; @@ -660,31 +660,31 @@ describe(EStepperView, () => { expect(view.root.findAllByType(ProgramArrows)).toHaveLength(0); }); - test("draws the environments with it, at the current step", () => { + test('draws the environments with it, at the current step', () => { const cseDiagram = service(); const view = render({ steps: withCse, profile, cseDiagram }); - expect(view.root.findAllByType(konva("konva-stage"))).toHaveLength(0); - expect(view.root.findByType(konva("host-cse-view")).props["data-step"]).toBe(0); + expect(view.root.findAllByType(konva('konva-stage'))).toHaveLength(0); + expect(view.root.findByType(konva('host-cse-view')).props['data-step']).toBe(0); const call = cseDiagram.createView.mock.calls.at(-1)![0]; expect(call.snapshots).toHaveLength(withCse.length); act(() => view.root - .find(n => hasClass(n, "sa-e-stepper")) - .props.onKeyDown({ key: "e", preventDefault: () => {} }), + .find(n => hasClass(n, 'sa-e-stepper')) + .props.onKeyDown({ key: 'e', preventDefault: () => {} }), ); - expect(view.root.findByType(konva("host-cse-view")).props["data-step"]).toBe( + expect(view.root.findByType(konva('host-cse-view')).props['data-step']).toBe( withCse.length - 1, ); }); - test("falls back to its own diagram when the steps carry no CSE snapshots", () => { + test('falls back to its own diagram when the steps carry no CSE snapshots', () => { const cseDiagram = service(); const view = render({ steps: fixture, profile, cseDiagram }); expect(cseDiagram.createView).not.toHaveBeenCalled(); - expect(view.root.findAllByType(konva("konva-stage"))).toHaveLength(1); + expect(view.root.findAllByType(konva('konva-stage'))).toHaveLength(1); }); - test("gives it the frame colours, and shares the hovered frame with it", () => { + test('gives it the frame colours, and shares the hovered frame with it', () => { const cseDiagram = service(); // From the step inside the call of withdraw, which shows an environment bracket. const view = render({ steps: withCse.slice(1), profile, cseDiagram }); @@ -692,8 +692,8 @@ describe(EStepperView, () => { const first = withCse[1]; expect(Object.keys(props().frameColors ?? {})).toEqual(first.frames.map(f => f.id)); // The program's environment blocks report hovering... - const label = view.root.findAll(n => hasClass(n, "estepper-envblock-label"))[0]; - const block = view.root.findAll(n => hasClass(n, "estepper-envblock"))[0]; + const label = view.root.findAll(n => hasClass(n, 'estepper-envblock-label'))[0]; + const block = view.root.findAll(n => hasClass(n, 'estepper-envblock'))[0]; const stopPropagation = vi.fn(); act(() => block.props.onMouseOver({ stopPropagation })); expect(props().hoveredFrame).toBe(text(label)); @@ -704,19 +704,19 @@ describe(EStepperView, () => { // ...and a frame hovered in the diagram is highlighted in the program. act(() => props().onHoverFrame!(text(label))); expect( - view.root.findAll(n => hasClass(n, "estepper-envblock") && hasClass(n, "hovered")), + view.root.findAll(n => hasClass(n, 'estepper-envblock') && hasClass(n, 'hovered')), ).not.toHaveLength(0); }); - test("shares the hovered object with it", () => { + test('shares the hovered object with it', () => { const cseDiagram = service(); const view = render({ steps: withCse, profile, cseDiagram }); const { onHover } = cseDiagram.createView.mock.calls.at(-1)![0]; - act(() => onHover!("#1")); - expect(cseDiagram.createView.mock.calls.at(-1)![0].hovered).toBe("#1"); + act(() => onHover!('#1')); + expect(cseDiagram.createView.mock.calls.at(-1)![0].hovered).toBe('#1'); act(() => onHover!(null)); expect(cseDiagram.createView.mock.calls.at(-1)![0].hovered).toBeNull(); - expect(view.root.findAllByType(konva("konva-stage"))).toHaveLength(0); + expect(view.root.findAllByType(konva('konva-stage'))).toHaveLength(0); }); }); }); diff --git a/src/web/e-stepper/src/__tests__/EnvDiagram.test.tsx b/src/web/e-stepper/src/__tests__/EnvDiagram.test.tsx index 4ee2948..5e6f70b 100644 --- a/src/web/e-stepper/src/__tests__/EnvDiagram.test.tsx +++ b/src/web/e-stepper/src/__tests__/EnvDiagram.test.tsx @@ -1,12 +1,12 @@ -import type { EStepperStep } from "@sourceacademy/common-e-stepper"; -import { act, createElement } from "react"; -import TestRenderer from "react-test-renderer"; -import { describe, expect, test, vi } from "vitest"; +import type { EStepperStep } from '@sourceacademy/common-e-stepper'; +import { act, createElement } from 'react'; +import TestRenderer from 'react-test-renderer'; +import { describe, expect, test, vi } from 'vitest'; -import EnvDiagram from "../EnvDiagram"; -import { DiagramColors } from "../colors"; -import { layoutDiagram } from "../layout"; -import steps from "./makeWithdrawSteps.json"; +import EnvDiagram from '../EnvDiagram'; +import { DiagramColors } from '../colors'; +import { layoutDiagram } from '../layout'; +import steps from './makeWithdrawSteps.json'; // Konva's Node build needs the optional `canvas` package, so the shapes are stubbed — as plain // host elements (`konva-rect`, ...) that keep their props and render their children, so the @@ -16,7 +16,7 @@ vi.mock(import('react-konva'), () => { const stub = (name: string) => (props: Record) => createElement(`konva-${name.toLowerCase()}`, props, props.children as never); return Object.fromEntries( - ["Stage", "Layer", "Text", "Group", "Line", "Rect", "Circle", "Arrow"].map(n => [n, stub(n)]), + ['Stage', 'Layer', 'Text', 'Group', 'Line', 'Rect', 'Circle', 'Arrow'].map(n => [n, stub(n)]), ); }); @@ -48,54 +48,54 @@ function draw(overrides: Partial[0]> = {}) { } const texts = (root: TestRenderer.ReactTestInstance) => - root.findAllByType(konva("konva-text")).map(t => String(t.props.text)); + root.findAllByType(konva('konva-text')).map(t => String(t.props.text)); describe(EnvDiagram, () => { - test("draws every frame with its label and bindings", () => { + test('draws every frame with its label and bindings', () => { const all = texts(draw().root); - expect(all).toEqual(expect.arrayContaining(["Global", "E1 make_withdraw", "E2 withdraw"])); - expect(all).toEqual(expect.arrayContaining(["balance:", "50", "amount:"])); + expect(all).toEqual(expect.arrayContaining(['Global', 'E1 make_withdraw', 'E2 withdraw'])); + expect(all).toEqual(expect.arrayContaining(['balance:', '50', 'amount:'])); }); - test("outlines the active frame", () => { + test('outlines the active frame', () => { const frameRects = draw() - .root.findAllByType(konva("konva-rect")) + .root.findAllByType(konva('konva-rect')) .filter(r => r.props.cornerRadius === 6); const widths = frameRects.map(r => r.props.strokeWidth); expect(widths.filter(w => w === 4)).toHaveLength(1); }); - test("draws function objects as two circles and lists as boxes with their elements", () => { + test('draws function objects as two circles and lists as boxes with their elements', () => { const { root } = draw(); const all = texts(root); - expect(all).toEqual(expect.arrayContaining(["#1 make_withdraw", "#2 withdraw", "#4", "#3"])); - expect(all).toEqual(expect.arrayContaining(["1", "2"])); - expect(root.findAllByType(konva("konva-arrow")).length).toBeGreaterThan(5); + expect(all).toEqual(expect.arrayContaining(['#1 make_withdraw', '#2 withdraw', '#4', '#3'])); + expect(all).toEqual(expect.arrayContaining(['1', '2'])); + expect(root.findAllByType(konva('konva-arrow')).length).toBeGreaterThan(5); }); - test("highlights the bindings a step reads", () => { - const { root } = draw({ lookups: [{ frameId: "E1", name: "balance" }] }); + test('highlights the bindings a step reads', () => { + const { root } = draw({ lookups: [{ frameId: 'E1', name: 'balance' }] }); const highlights = root - .findAllByType(konva("konva-rect")) + .findAllByType(konva('konva-rect')) .filter(r => r.props.fill === DiagramColors.lookup); expect(highlights).toHaveLength(1); }); - test("dims garbage and reports hovering over an object", () => { + test('dims garbage and reports hovering over an object', () => { const last = (steps as unknown as EStepperStep[])[2]; const { root, onHover } = draw({ frames: last.frames, heap: last.heap, - activeFrameId: "Global", + activeFrameId: 'Global', }); const dimmed = root - .findAllByType(konva("konva-group")) + .findAllByType(konva('konva-group')) .filter(g => g.props.opacity === DiagramColors.garbageOpacity); expect(dimmed.length).toBeGreaterThan(0); // Frames report hovering through onHoverFrame (not given here), objects through onHover. const object = root - .findAllByType(konva("konva-group")) - .filter(g => typeof g.props.onMouseEnter === "function") + .findAllByType(konva('konva-group')) + .filter(g => typeof g.props.onMouseEnter === 'function') .find(g => { act(() => g.props.onMouseEnter()); return onHover.mock.calls.length > 0; @@ -105,40 +105,40 @@ describe(EnvDiagram, () => { expect(onHover).toHaveBeenLastCalledWith(null); }); - test("draws dead frames, their data and their arrows grey, and live ones in their colours", () => { + test('draws dead frames, their data and their arrows grey, and live ones in their colours', () => { const last = (steps as unknown as EStepperStep[])[2]; - const { root } = draw({ frames: last.frames, heap: last.heap, activeFrameId: "Global" }); + const { root } = draw({ frames: last.frames, heap: last.heap, activeFrameId: 'Global' }); const outlines = root - .findAllByType(konva("konva-rect")) + .findAllByType(konva('konva-rect')) .filter(r => r.props.cornerRadius === 6) .map(r => r.props.stroke); expect(outlines.filter(c => c === DiagramColors.garbage)).toHaveLength( last.frames.filter(f => f.isGarbage).length, ); expect(new Set(outlines).size).toBeGreaterThan(1); - const arrows = root.findAllByType(konva("konva-arrow")).map(a => a.props.stroke); + const arrows = root.findAllByType(konva('konva-arrow')).map(a => a.props.stroke); expect(arrows).toContain(DiagramColors.garbage); expect(arrows).toContain(DiagramColors.stroke); }); - test("draws the values in a dead frame grey too, not just its names", () => { + test('draws the values in a dead frame grey too, not just its names', () => { const frame = (id: string, parentId: string | null, isGarbage: boolean) => ({ id, - name: id === "Global" ? "global" : "f", + name: id === 'Global' ? 'global' : 'f', parentId, - bindings: [{ name: "n", value: { kind: "primitive" as const, display: "77", label: "int" } }], + bindings: [{ name: 'n', value: { kind: 'primitive' as const, display: '77', label: 'int' } }], isGarbage, }); const { root } = draw({ - frames: [frame("Global", null, false), frame("E1", "Global", true)], + frames: [frame('Global', null, false), frame('E1', 'Global', true)], heap: [], - activeFrameId: "Global", + activeFrameId: 'Global', }); - const values = root.findAllByType(konva("konva-text")).filter(t => t.props.text === "77"); + const values = root.findAllByType(konva('konva-text')).filter(t => t.props.text === '77'); expect(values.map(t => t.props.fill)).toEqual([DiagramColors.text, DiagramColors.garbage]); }); - describe("anchors for arrows from the program", () => { + describe('anchors for arrows from the program', () => { const anchored = () => { const onAnchors = vi.fn(); const view = draw({ onAnchors }); @@ -146,11 +146,11 @@ describe(EnvDiagram, () => { return { ...view, onAnchors, latest }; }; - test("tell where an object or a frame is drawn, and where it is not", () => { + test('tell where an object or a frame is drawn, and where it is not', () => { const { latest } = anchored(); const resolve = latest(); - const frame = resolve({ kind: "frame", id: "E1" }); - const object = resolve({ kind: "object", id: "#1" }); + const frame = resolve({ kind: 'frame', id: 'E1' }); + const object = resolve({ kind: 'object', id: '#1' }); expect(frame).toEqual({ x: expect.any(Number), y: expect.any(Number), @@ -161,53 +161,53 @@ describe(EnvDiagram, () => { y: expect.any(Number), scale: expect.any(Number), }); - expect(resolve({ kind: "frame", id: "nope" })).toBeNull(); - expect(resolve({ kind: "object", id: "nope" })).toBeNull(); + expect(resolve({ kind: 'frame', id: 'nope' })).toBeNull(); + expect(resolve({ kind: 'object', id: 'nope' })).toBeNull(); }); - test("end at the horizontal centre of the top of an object, and the left edge of a frame", () => { + test('end at the horizontal centre of the top of an object, and the left edge of a frame', () => { const { latest } = anchored(); const layout = layoutDiagram(step.frames, step.heap); const view = { scale: Math.min(1, 800 / layout.width, 600 / layout.height) }; for (const box of layout.objects) { - const at = latest()({ kind: "object", id: box.object.id }); + const at = latest()({ kind: 'object', id: box.object.id }); expect(at.x).toBeCloseTo((box.x + box.width / 2) * view.scale); expect(at.y).toBeCloseTo(box.y * view.scale); } const frame = layout.frames[1]; - const at = latest()({ kind: "frame", id: frame.frame.id }); + const at = latest()({ kind: 'frame', id: frame.frame.id }); expect(at.x).toBeCloseTo(frame.x * view.scale); }); test("follow the user's pan and zoom, and go away with the diagram", () => { const { root, onAnchors, latest } = anchored(); - const before = latest()({ kind: "frame", id: "E1" }); + const before = latest()({ kind: 'frame', id: 'E1' }); act(() => root - .findByType(konva("konva-stage")) + .findByType(konva('konva-stage')) .props.onDragMove({ target: { x: () => 30, y: () => 40 } }), ); - const panned = latest()({ kind: "frame", id: "E1" }); + const panned = latest()({ kind: 'frame', id: 'E1' }); expect(panned).toEqual({ x: before.x + 30, y: before.y + 40, scale: before.scale }); act(() => root - .findByType(konva("konva-stage")) + .findByType(konva('konva-stage')) .props.onDragEnd({ target: { x: () => 30, y: () => 40 } }), ); const wheel = { evt: { deltaY: -1, preventDefault: vi.fn() }, target: { getStage: () => ({ getPointerPosition: () => ({ x: 0, y: 0 }) }) }, }; - act(() => root.findByType(konva("konva-stage")).props.onWheel(wheel)); + act(() => root.findByType(konva('konva-stage')).props.onWheel(wheel)); expect(wheel.evt.preventDefault).toHaveBeenCalled(); - const zoomed = latest()({ kind: "frame", id: "E1" }); + const zoomed = latest()({ kind: 'frame', id: 'E1' }); expect(zoomed).not.toEqual(panned); // The arrowheads follow the zoom. expect(zoomed.scale).toBeGreaterThan(panned.scale); expect(onAnchors).not.toHaveBeenLastCalledWith(null); }); - test("end when the diagram unmounts", () => { + test('end when the diagram unmounts', () => { const onAnchors = vi.fn(); let renderer!: TestRenderer.ReactTestRenderer; act(() => { @@ -230,22 +230,22 @@ describe(EnvDiagram, () => { }); }); - test("reports hovering over a frame, and draws a hovered frame highlighted", () => { + test('reports hovering over a frame, and draws a hovered frame highlighted', () => { const onHoverFrame = vi.fn(); - const { root } = draw({ onHoverFrame, hoveredFrame: "E2" }); + const { root } = draw({ onHoverFrame, hoveredFrame: 'E2' }); const frameGroups = root - .findAllByType(konva("konva-group")) - .filter(g => typeof g.props.onMouseEnter === "function" && "opacity" in g.props); + .findAllByType(konva('konva-group')) + .filter(g => typeof g.props.onMouseEnter === 'function' && 'opacity' in g.props); act(() => frameGroups[0].props.onMouseEnter()); expect(onHoverFrame).toHaveBeenLastCalledWith(step.frames[0].id); act(() => frameGroups[0].props.onMouseLeave()); expect(onHoverFrame).toHaveBeenLastCalledWith(null); const highlighted = root - .findAllByType(konva("konva-rect")) + .findAllByType(konva('konva-rect')) .filter(r => r.props.fill === DiagramColors.hoverBackground); expect(highlighted).toHaveLength(1); // Every frame box is filled, so its whole area takes the mouse. - const boxes = root.findAllByType(konva("konva-rect")).filter(r => r.props.cornerRadius === 6); + const boxes = root.findAllByType(konva('konva-rect')).filter(r => r.props.cornerRadius === 6); expect(boxes.length).toBe(step.frames.length); for (const box of boxes) expect(box.props.fill).toBeTruthy(); }); @@ -254,57 +254,57 @@ describe(EnvDiagram, () => { const { root } = draw({ frames: [ { - id: "Global", - name: "global", + id: 'Global', + name: 'global', parentId: null, isGarbage: false, bindings: [ - { name: "xs", value: { kind: "ref", objectId: "#1" } }, - { name: "y", value: { kind: "primitive", display: "None", label: "None" } }, + { name: 'xs', value: { kind: 'ref', objectId: '#1' } }, + { name: 'y', value: { kind: 'primitive', display: 'None', label: 'None' } }, ], }, ], heap: [ { - kind: "list", - id: "#1", + kind: 'list', + id: '#1', isGarbage: false, elements: [ - { kind: "primitive", display: "3", label: "int" }, + { kind: 'primitive', display: '3', label: 'int' }, // Whatever the type tag: py-slang sends "None", others may send "NoneType". - { kind: "primitive", display: "None", label: "None" }, - { kind: "primitive", display: "None", label: "NoneType" }, + { kind: 'primitive', display: 'None', label: 'None' }, + { kind: 'primitive', display: 'None', label: 'NoneType' }, // A string reading None is not None. - { kind: "primitive", display: "'None'", label: "str" }, + { kind: 'primitive', display: "'None'", label: 'str' }, ], }, ] as never, - activeFrameId: "Global", + activeFrameId: 'Global', }); - const texts = root.findAllByType(konva("konva-text")).map(t => t.props.text); + const texts = root.findAllByType(konva('konva-text')).map(t => t.props.text); // The binding y shows None; the list's box does not. - expect(texts.filter(t => t === "None")).toHaveLength(1); - expect(texts).toContain("3"); + expect(texts.filter(t => t === 'None')).toHaveLength(1); + expect(texts).toContain('3'); expect(texts).toContain("'None'"); const slashes = root - .findAllByType(konva("konva-line")) + .findAllByType(konva('konva-line')) .filter(l => l.props.points[1] > l.props.points[3]); expect(slashes).toHaveLength(2); }); - test("a hovered object gets a darker background, keeping its outline", () => { - const { root } = draw({ hovered: "#2" }); + test('a hovered object gets a darker background, keeping its outline', () => { + const { root } = draw({ hovered: '#2' }); const darkened = root - .findAllByType(konva("konva-circle")) + .findAllByType(konva('konva-circle')) .filter(c => c.props.fill === DiagramColors.hoverBackground); // The function object's two circles, outlined as usual. expect(darkened).toHaveLength(2); for (const c of darkened) expect(c.props.stroke).toBe(DiagramColors.stroke); }); - test("zooms around the pointer with the mouse wheel and remembers a pan", () => { + test('zooms around the pointer with the mouse wheel and remembers a pan', () => { const { root } = draw(); - const stage = root.findByType(konva("konva-stage")); + const stage = root.findByType(konva('konva-stage')); const wheel = (deltaY: number) => act(() => stage.props.onWheel({ @@ -312,14 +312,14 @@ describe(EnvDiagram, () => { target: { getStage: () => ({ getPointerPosition: () => ({ x: 100, y: 100 }) }) }, }), ); - const scale = () => root.findByType(konva("konva-stage")).props.scaleX as number; + const scale = () => root.findByType(konva('konva-stage')).props.scaleX as number; const before = scale(); wheel(-1); expect(scale()).toBeCloseTo(before * 1.1); wheel(1); expect(scale()).toBeCloseTo(before); act(() => stage.props.onDragEnd({ target: { x: () => 30, y: () => 40 } })); - expect(root.findByType(konva("konva-stage")).props.x).toBe(30); - expect(root.findByType(konva("konva-stage")).props.y).toBe(40); + expect(root.findByType(konva('konva-stage')).props.x).toBe(30); + expect(root.findByType(konva('konva-stage')).props.y).toBe(40); }); }); diff --git a/src/web/e-stepper/src/__tests__/ProgramArrows.test.tsx b/src/web/e-stepper/src/__tests__/ProgramArrows.test.tsx index 53f520a..9a3a092 100644 --- a/src/web/e-stepper/src/__tests__/ProgramArrows.test.tsx +++ b/src/web/e-stepper/src/__tests__/ProgramArrows.test.tsx @@ -1,13 +1,13 @@ -import { act } from "react"; -import TestRenderer from "react-test-renderer"; -import { describe, expect, test, vi } from "vitest"; +import { act } from 'react'; +import TestRenderer from 'react-test-renderer'; +import { describe, expect, test, vi } from 'vitest'; import ProgramArrows, { ENV_ATTRIBUTE, REF_ATTRIBUTE, arrowGeometry, computeProgramArrows, -} from "../ProgramArrows"; +} from '../ProgramArrows'; const rect = (left: number, top: number, width: number, height: number) => ({ left, top, right: left + width, bottom: top + height, width, height }) as DOMRect; @@ -25,50 +25,50 @@ function fake(box: DOMRect, attributes: Record = {}, marks: unkn } describe(arrowGeometry, () => { - test("leaves and arrives horizontally when the target is further across than down", () => { + test('leaves and arrives horizontally when the target is further across than down', () => { const { path, head } = arrowGeometry({ x: 0, y: 0 }, { x: 100, y: 20 }); - expect(path).toBe("M 0 0 C 50 0, 50 20, 100 20"); - expect(head.startsWith("100 20 ")).toBe(true); + expect(path).toBe('M 0 0 C 50 0, 50 20, 100 20'); + expect(head.startsWith('100 20 ')).toBe(true); }); - test("goes vertically when the panes are stacked", () => { + test('goes vertically when the panes are stacked', () => { const { path } = arrowGeometry({ x: 10, y: 0 }, { x: 0, y: 200 }); - expect(path).toBe("M 10 0 C 10 100, 0 100, 0 200"); + expect(path).toBe('M 10 0 C 10 100, 0 100, 0 200'); }); - test("comes down onto the top of an object, whatever the layout", () => { + test('comes down onto the top of an object, whatever the layout', () => { // Side by side (further across than down): it still arrives vertically, from above. - const { path } = arrowGeometry({ x: 0, y: 0 }, { x: 200, y: 40 }, "above"); - expect(path).toBe("M 0 0 C 100 0, 200 20, 200 40"); + const { path } = arrowGeometry({ x: 0, y: 0 }, { x: 200, y: 40 }, 'above'); + expect(path).toBe('M 0 0 C 100 0, 200 20, 200 40'); }); - test("arrives at a frame from the left, whatever the layout", () => { + test('arrives at a frame from the left, whatever the layout', () => { // Side by side: a horizontal arrival, as before. - expect(arrowGeometry({ x: 0, y: 0 }, { x: 100, y: 20 }, "left").path).toBe( - "M 0 0 C 50 0, 50 20, 100 20", + expect(arrowGeometry({ x: 0, y: 0 }, { x: 100, y: 20 }, 'left').path).toBe( + 'M 0 0 C 50 0, 50 20, 100 20', ); // Stacked: it leaves downwards, but still comes into the left edge, pointing right. - const stacked = arrowGeometry({ x: 10, y: 0 }, { x: 80, y: 300 }, "left"); - expect(stacked.path).toBe("M 10 0 C 10 120, 45 300, 80 300"); - expect(stacked.head.startsWith("80 300 ")).toBe(true); + const stacked = arrowGeometry({ x: 10, y: 0 }, { x: 80, y: 300 }, 'left'); + expect(stacked.path).toBe('M 10 0 C 10 120, 45 300, 80 300'); + expect(stacked.head.startsWith('80 300 ')).toBe(true); }); - test("a frame to the left of the start is still entered from its left", () => { - expect(arrowGeometry({ x: 100, y: 0 }, { x: 0, y: 200 }, "left").path).toBe( - "M 100 0 C 100 100, -40 200, 0 200", + test('a frame to the left of the start is still entered from its left', () => { + expect(arrowGeometry({ x: 100, y: 0 }, { x: 0, y: 200 }, 'left').path).toBe( + 'M 100 0 C 100 100, -40 200, 0 200', ); }); - test("bends back correctly when the target is to the left", () => { - expect(arrowGeometry({ x: 100, y: 0 }, { x: 0, y: 0 }).path).toBe("M 100 0 C 50 0, 50 0, 0 0"); + test('bends back correctly when the target is to the left', () => { + expect(arrowGeometry({ x: 100, y: 0 }, { x: 0, y: 0 }).path).toBe('M 100 0 C 50 0, 50 0, 0 0'); }); }); -describe("arrowGeometry scale", () => { +describe('arrowGeometry scale', () => { test("the arrowhead grows with the diagram's zoom", () => { const size = (scale: number) => { - const { head } = arrowGeometry({ x: 0, y: 0 }, { x: 100, y: 0 }, "axis", scale); - return head.split(" ").map(Number); + const { head } = arrowGeometry({ x: 0, y: 0 }, { x: 100, y: 0 }, 'axis', scale); + return head.split(' ').map(Number); }; // Tip at (100, 0); the base is HEAD_LENGTH * scale back from it. expect(size(1)).toEqual([100, 0, 92, 4, 92, -4]); @@ -81,33 +81,33 @@ describe(computeProgramArrows, () => { const program = (marks: unknown[]) => fake(rect(100, 50, 300, 400), {}, marks); const diagram = fake(rect(500, 50, 400, 400)); const resolve = ({ kind, id }: { kind: string; id: string }) => - id === "missing" ? null : kind === "object" ? { x: 10, y: 20 } : { x: 30, y: 40 }; + id === 'missing' ? null : kind === 'object' ? { x: 10, y: 20 } : { x: 30, y: 40 }; - test("draws an arrow from each badge and bracket label to its target, in container coordinates", () => { - const ref = fake(rect(150, 100, 20, 10), { [REF_ATTRIBUTE]: "#1" }); - const env = fake(rect(120, 200, 20, 12), { [ENV_ATTRIBUTE]: "E1" }); + test('draws an arrow from each badge and bracket label to its target, in container coordinates', () => { + const ref = fake(rect(150, 100, 20, 10), { [REF_ATTRIBUTE]: '#1' }); + const env = fake(rect(120, 200, 20, 12), { [ENV_ATTRIBUTE]: 'E1' }); const arrows = computeProgramArrows(container, program([ref, env]), diagram, resolve); expect(arrows.map(a => [a.kind, a.id, a.from, a.to])).toEqual([ - ["object", "#1", { x: 60, y: 55 }, { x: 410, y: 20 }], - ["frame", "E1", { x: 30, y: 156 }, { x: 430, y: 40 }], + ['object', '#1', { x: 60, y: 55 }, { x: 410, y: 20 }], + ['frame', 'E1', { x: 30, y: 156 }, { x: 430, y: 40 }], ]); }); - test("carries the zoom of the target, 1 when the host gives none", () => { - const ref = fake(rect(150, 100, 20, 10), { [REF_ATTRIBUTE]: "#1" }); - const env = fake(rect(120, 200, 20, 12), { [ENV_ATTRIBUTE]: "E1" }); + test('carries the zoom of the target, 1 when the host gives none', () => { + const ref = fake(rect(150, 100, 20, 10), { [REF_ATTRIBUTE]: '#1' }); + const env = fake(rect(120, 200, 20, 12), { [ENV_ATTRIBUTE]: 'E1' }); const zoomed = ({ kind }: { kind: string }) => - kind === "object" ? { x: 10, y: 20, scale: 1.5 } : { x: 30, y: 40 }; + kind === 'object' ? { x: 10, y: 20, scale: 1.5 } : { x: 30, y: 40 }; const arrows = computeProgramArrows(container, program([ref, env]), diagram, zoomed); expect(arrows.map(a => a.scale)).toEqual([1.5, 1]); }); - test("skips marks scrolled out of the program pane, and targets that are not drawn or not in view", () => { - const scrolledOut = fake(rect(150, 600, 20, 10), { [REF_ATTRIBUTE]: "#1" }); - const notDrawn = fake(rect(150, 100, 20, 10), { [REF_ATTRIBUTE]: "missing" }); - const outOfView = fake(rect(150, 100, 20, 10), { [REF_ATTRIBUTE]: "#3" }); + test('skips marks scrolled out of the program pane, and targets that are not drawn or not in view', () => { + const scrolledOut = fake(rect(150, 600, 20, 10), { [REF_ATTRIBUTE]: '#1' }); + const notDrawn = fake(rect(150, 100, 20, 10), { [REF_ATTRIBUTE]: 'missing' }); + const outOfView = fake(rect(150, 100, 20, 10), { [REF_ATTRIBUTE]: '#3' }); const far = ({ id }: { id: string }) => - id === "#3" ? { x: 900, y: 20 } : id === "missing" ? null : { x: 1, y: 1 }; + id === '#3' ? { x: 900, y: 20 } : id === 'missing' ? null : { x: 1, y: 1 }; expect( computeProgramArrows(container, program([scrolledOut, notDrawn, outOfView]), diagram, far), ).toHaveLength(0); @@ -115,8 +115,8 @@ describe(computeProgramArrows, () => { }); describe(ProgramArrows, () => { - const ref = fake(rect(150, 100, 20, 10), { [REF_ATTRIBUTE]: "#1" }); - const env = fake(rect(120, 200, 20, 12), { [ENV_ATTRIBUTE]: "E1" }); + const ref = fake(rect(150, 100, 20, 10), { [REF_ATTRIBUTE]: '#1' }); + const env = fake(rect(120, 200, 20, 12), { [ENV_ATTRIBUTE]: 'E1' }); const listeners = new Map void>(); const programEl = Object.assign(fake(rect(100, 50, 300, 400), {}, [ref, env]), { addEventListener: (type: string, fn: () => void) => listeners.set(type, fn), @@ -126,24 +126,24 @@ describe(ProgramArrows, () => { const containerEl = { getBoundingClientRect: () => rect(100, 50, 800, 400), querySelector: (selector: string) => - selector === ".estepper-program" + selector === '.estepper-program' ? programEl - : selector === ".estepper-diagram" + : selector === '.estepper-diagram' ? diagramEl : null, }; function mount(props: Partial[0]> = {}) { - vi.stubGlobal("window", { addEventListener: vi.fn(), removeEventListener: vi.fn() }); + vi.stubGlobal('window', { addEventListener: vi.fn(), removeEventListener: vi.fn() }); let renderer!: TestRenderer.ReactTestRenderer; act(() => { renderer = TestRenderer.create( (kind === "object" ? { x: 10, y: 20 } : { x: 30, y: 40 })} + resolve={({ kind }) => (kind === 'object' ? { x: 10, y: 20 } : { x: 30, y: 40 })} hovered={null} hoveredFrame={null} all - colorOfFrame={() => "#ff0000"} + colorOfFrame={() => '#ff0000'} {...props} />, { createNodeMock: () => ({ parentElement: containerEl }) }, @@ -153,41 +153,41 @@ describe(ProgramArrows, () => { } const strokes = (r: TestRenderer.ReactTestRenderer) => r.root - .findAllByType("path") + .findAllByType('path') .map(p => [p.props.stroke, p.props.strokeWidth, p.parent!.props.opacity]); - test("draws an arrow to each object, grey; one to a frame only while it is hovered", () => { + test('draws an arrow to each object, grey; one to a frame only while it is hovered', () => { const view = mount(); - expect(view.root.findAllByType("polygon")).toHaveLength(1); - expect(strokes(view)).toEqual([["#c5cbd3", 1.5, 0.55]]); + expect(view.root.findAllByType('polygon')).toHaveLength(1); + expect(strokes(view)).toEqual([['#c5cbd3', 1.5, 0.55]]); act(() => view.unmount()); vi.unstubAllGlobals(); }); test('without "all", an arrow to an object is drawn only while the object is hovered', () => { expect(strokes(mount({ all: false }))).toEqual([]); - expect(strokes(mount({ all: false, hovered: "#2" }))).toEqual([]); - expect(strokes(mount({ all: false, hovered: "#1" }))).toEqual([["#c5cbd3", 2.5, 1]]); + expect(strokes(mount({ all: false, hovered: '#2' }))).toEqual([]); + expect(strokes(mount({ all: false, hovered: '#1' }))).toEqual([['#c5cbd3', 2.5, 1]]); vi.unstubAllGlobals(); }); test('an arrow to a frame is drawn in the frame\'s colour while the frame is hovered, with or without "all"', () => { for (const all of [true, false]) { - const view = mount({ all, hoveredFrame: "E1" }); - expect(strokes(view).at(-1)).toEqual(["#ff0000", 2.5, 1]); - expect(view.root.findAllByType("path")).toHaveLength(all ? 2 : 1); + const view = mount({ all, hoveredFrame: 'E1' }); + expect(strokes(view).at(-1)).toEqual(['#ff0000', 2.5, 1]); + expect(view.root.findAllByType('path')).toHaveLength(all ? 2 : 1); } vi.unstubAllGlobals(); }); - test("draws none without anchors, and measures again when the program pane scrolls", () => { + test('draws none without anchors, and measures again when the program pane scrolls', () => { expect(strokes(mount({ resolve: null }))).toEqual([]); const view = mount(); - expect(listeners.has("scroll")).toBe(true); - act(() => listeners.get("scroll")!()); - expect(view.root.findAllByType("path")).toHaveLength(1); + expect(listeners.has('scroll')).toBe(true); + act(() => listeners.get('scroll')!()); + expect(view.root.findAllByType('path')).toHaveLength(1); act(() => view.unmount()); - expect(listeners.has("scroll")).toBe(false); + expect(listeners.has('scroll')).toBe(false); vi.unstubAllGlobals(); }); }); diff --git a/src/web/e-stepper/src/__tests__/layout.test.ts b/src/web/e-stepper/src/__tests__/layout.test.ts index a28f271..c4e83b6 100644 --- a/src/web/e-stepper/src/__tests__/layout.test.ts +++ b/src/web/e-stepper/src/__tests__/layout.test.ts @@ -2,12 +2,12 @@ import type { EStepperFrame, EStepperHeapObject, EStepperStep, -} from "@sourceacademy/common-e-stepper"; -import { describe, expect, test } from "vitest"; +} from '@sourceacademy/common-e-stepper'; +import { describe, expect, test } from 'vitest'; -import { frameColor } from "../colors"; -import { LayoutConfig as C, layoutDiagram, type DiagramLayout } from "../layout"; -import steps from "./makeWithdrawSteps.json" with { type: 'json' }; +import { frameColor } from '../colors'; +import { LayoutConfig as C, layoutDiagram, type DiagramLayout } from '../layout'; +import steps from './makeWithdrawSteps.json' with { type: 'json' }; /** The last step of `make_withdraw` (produced by py-slang's e-stepper engine): frames Global, E1, * E2 (garbage), function objects #1 (in Global) and #2 (in E1), lists #4 = [W1, #3] and #3. */ @@ -37,21 +37,21 @@ function rects(layout: DiagramLayout): Rect[] { const overlaps = (a: Rect, b: Rect) => a.x < b.x + b.width && b.x < a.x + a.width && a.y < b.y + b.height && b.y < a.y + a.height; -describe("layoutDiagram on make_withdraw", () => { +describe('layoutDiagram on make_withdraw', () => { const layout = layoutDiagram(last.frames, last.heap); const frame = (id: string) => layout.frames.find(f => f.frame.id === id)!; const object = (id: string) => layout.objects.find(o => o.object.id === id)!; - test("frames are in rows by nesting depth", () => { - expect(layout.frames.map(f => f.frame.id)).toEqual(["Global", "E1", "E2"]); - expect(frame("Global").y).toBeLessThan(frame("E1").y); - expect(frame("E1").y).toBeLessThan(frame("E2").y); + test('frames are in rows by nesting depth', () => { + expect(layout.frames.map(f => f.frame.id)).toEqual(['Global', 'E1', 'E2']); + expect(frame('Global').y).toBeLessThan(frame('E1').y); + expect(frame('E1').y).toBeLessThan(frame('E2').y); }); - test("function objects sit to the right of the frame they were defined in", () => { + test('function objects sit to the right of the frame they were defined in', () => { for (const [fn, env] of [ - ["#1", "Global"], - ["#2", "E1"], + ['#1', 'Global'], + ['#2', 'E1'], ]) { expect(object(fn).x).toBeGreaterThan(frame(env).x + frame(env).width); expect(object(fn).y).toBeGreaterThanOrEqual(frame(env).y); @@ -59,16 +59,16 @@ describe("layoutDiagram on make_withdraw", () => { } }); - test("lists sit with the first frame that refers to them, nested lists alongside", () => { - const global = frame("Global"); - for (const id of ["#4", "#3"]) { + test('lists sit with the first frame that refers to them, nested lists alongside', () => { + const global = frame('Global'); + for (const id of ['#4', '#3']) { expect(object(id).x).toBeGreaterThan(global.x + global.width); - expect(object(id).y).toBeLessThan(frame("E1").y); + expect(object(id).y).toBeLessThan(frame('E1').y); } - expect(object("#4").width).toBe(2 * C.cellWidth); + expect(object('#4').width).toBe(2 * C.cellWidth); }); - test("nothing overlaps, and everything is inside the drawing", () => { + test('nothing overlaps, and everything is inside the drawing', () => { const all = rects(layout); for (let i = 0; i < all.length; i++) { for (let j = i + 1; j < all.length; j++) { @@ -79,59 +79,59 @@ describe("layoutDiagram on make_withdraw", () => { } }); - test("arrows: one per reference binding, list element, function environment and parent", () => { + test('arrows: one per reference binding, list element, function environment and parent', () => { const keys = layout.arrows.map(a => a.key).sort(); expect(keys).toEqual( [ - "binding:Global:make_withdraw", - "binding:Global:W1", - "binding:Global:xs", - "binding:E1:withdraw", - "element:#4:0", - "element:#4:1", - "environment:#1", - "environment:#2", - "parent:E1", - "parent:E2", + 'binding:Global:make_withdraw', + 'binding:Global:W1', + 'binding:Global:xs', + 'binding:E1:withdraw', + 'element:#4:0', + 'element:#4:1', + 'environment:#1', + 'environment:#2', + 'parent:E1', + 'parent:E2', ].sort(), ); // A binding arrow ends at the left edge of its object. - const w1 = layout.arrows.find(a => a.key === "binding:Global:W1")!; - expect(w1.to.x).toBe(object("#2").x); + const w1 = layout.arrows.find(a => a.key === 'binding:Global:W1')!; + expect(w1.to.x).toBe(object('#2').x); // An arrow from a garbage frame is dimmed. - expect(layout.arrows.find(a => a.key === "parent:E2")!.garbage).toBe(true); + expect(layout.arrows.find(a => a.key === 'parent:E2')!.garbage).toBe(true); }); }); -describe("layoutDiagram edge cases", () => { - test("objects no frame refers to go in a row of their own at the bottom", () => { +describe('layoutDiagram edge cases', () => { + test('objects no frame refers to go in a row of their own at the bottom', () => { const frames: EStepperFrame[] = [ - { id: "Global", name: "global", parentId: null, bindings: [], isGarbage: false }, + { id: 'Global', name: 'global', parentId: null, bindings: [], isGarbage: false }, ]; - const heap: EStepperHeapObject[] = [{ kind: "list", id: "#1", elements: [], isGarbage: false }]; + const heap: EStepperHeapObject[] = [{ kind: 'list', id: '#1', elements: [], isGarbage: false }]; const layout = layoutDiagram(frames, heap); expect(layout.objects[0].y).toBeGreaterThan(layout.frames[0].y + C.headerHeight); expect(layout.objects[0].width).toBe(C.cellWidth); }); - test("an empty store lays out to an empty drawing", () => { + test('an empty store lays out to an empty drawing', () => { const layout = layoutDiagram([], []); expect(layout.frames).toEqual([]); expect(layout.arrows).toEqual([]); }); }); -test("the global frame is neutral, other frames get distinct colours", () => { +test('the global frame is neutral, other frames get distinct colours', () => { const colors = [0, 1, 2, 3].map(frameColor); expect(new Set(colors).size).toBe(4); }); -describe("layoutDiagram with dead frames cleared", () => { +describe('layoutDiagram with dead frames cleared', () => { const full = layoutDiagram(last.frames, last.heap); const cleared = layoutDiagram(last.frames, last.heap, { clearDead: true }); const deadObjects = last.heap.filter(o => o.isGarbage).map(o => o.id); - test("garbage frames and objects are left out, and the rest stays", () => { + test('garbage frames and objects are left out, and the rest stays', () => { const dead = last.frames.filter(f => f.isGarbage).map(f => f.id); expect(dead.length).toBeGreaterThan(0); expect(cleared.frames.map(b => b.frame.id)).toEqual( @@ -142,34 +142,34 @@ describe("layoutDiagram with dead frames cleared", () => { ); }); - test("no arrow starts or ends at something that is gone", () => { + test('no arrow starts or ends at something that is gone', () => { expect(cleared.arrows.length).toBeLessThan(full.arrows.length); expect(cleared.arrows.every(a => !a.garbage)).toBe(true); }); - test("nothing changes when nothing is dead", () => { + test('nothing changes when nothing is dead', () => { const live = last.frames.map(f => ({ ...f, isGarbage: false })); const heap = last.heap.map(o => ({ ...o, isGarbage: false })); expect(layoutDiagram(live, heap, { clearDead: true })).toEqual(layoutDiagram(live, heap)); }); - test("a live frame keeps its colour index when dead frames before it are left out", () => { + test('a live frame keeps its colour index when dead frames before it are left out', () => { const frame = (id: string, isGarbage: boolean) => ({ id, - name: "f", - parentId: "Global", + name: 'f', + parentId: 'Global', bindings: [], isGarbage, }); const frames = [ - { ...frame("Global", false), name: "global", parentId: null }, - frame("E1", true), - frame("E2", false), + { ...frame('Global', false), name: 'global', parentId: null }, + frame('E1', true), + frame('E2', false), ]; const layout = layoutDiagram(frames, [], { clearDead: true }); expect(layout.frames.map(b => [b.frame.id, b.index])).toEqual([ - ["Global", 0], - ["E2", 2], + ['Global', 0], + ['E2', 2], ]); }); }); diff --git a/src/web/e-stepper/src/__tests__/styles.test.ts b/src/web/e-stepper/src/__tests__/styles.test.ts index 9b4929d..662a724 100644 --- a/src/web/e-stepper/src/__tests__/styles.test.ts +++ b/src/web/e-stepper/src/__tests__/styles.test.ts @@ -1,7 +1,7 @@ -import { afterEach, expect, test, vi } from "vitest"; +import { afterEach, expect, test, vi } from 'vitest'; -import { injectStepperStyles } from "../../../stepper/src/styles"; -import { injectEStepperStyles } from "../styles"; +import { injectStepperStyles } from '../../../stepper/src/styles'; +import { injectEStepperStyles } from '../styles'; /** A minimal `document`: the injectors only look up, create and append style elements. */ function fakeDocument() { @@ -12,7 +12,7 @@ function fakeDocument() { appendChild: (e: { id: string; textContent: string }) => (head.push(e), (elements[e.id] = e)), }, getElementById: (id: string) => elements[id] ?? null, - createElement: () => ({ id: "", textContent: "" }), + createElement: () => ({ id: '', textContent: '' }), appended: head, }; } @@ -20,42 +20,42 @@ function fakeDocument() { afterEach(() => vi.unstubAllGlobals()); test.each([ - ["e-stepper", injectEStepperStyles, ".sa-e-stepper"], - ["stepper", injectStepperStyles, ".sa-substituter"], + ['e-stepper', injectEStepperStyles, '.sa-e-stepper'], + ['stepper', injectStepperStyles, '.sa-substituter'], ])( - "the %s styles are injected once and kept up to date", + 'the %s styles are injected once and kept up to date', (_name: string, inject: () => void, selector: string) => { const doc = fakeDocument(); - vi.stubGlobal("document", doc); + vi.stubGlobal('document', doc); inject(); inject(); expect(doc.appended).toHaveLength(1); expect(doc.appended[0].textContent).toContain(selector); // A stale copy (e.g. from an earlier load of the plugin) is replaced. - doc.appended[0].textContent = "stale"; + doc.appended[0].textContent = 'stale'; inject(); expect(doc.appended[0].textContent).toContain(selector); }, ); -test("injecting without a document does nothing", () => { +test('injecting without a document does nothing', () => { expect(() => injectEStepperStyles()).not.toThrow(); expect(() => injectStepperStyles()).not.toThrow(); }); -test("the dividers between the panes never shrink away, however short the tab is", () => { +test('the dividers between the panes never shrink away, however short the tab is', () => { const doc = fakeDocument(); - vi.stubGlobal("document", doc); + vi.stubGlobal('document', doc); injectEStepperStyles(); const css = doc.appended[0].textContent; // In a stacked tab the program pane has a fixed height and the diagram a least height, so a flex // item that may shrink (the default) would be squeezed to nothing. const rule = (selector: string) => - css.slice(css.indexOf(selector), css.indexOf("}", css.indexOf(selector))); - expect(rule(".sa-e-stepper .estepper-divider {")).toContain("flex: 0 0 auto"); - expect(rule(".sa-e-stepper .estepper-divider.vertical")).toContain("flex: 0 0 auto"); + css.slice(css.indexOf(selector), css.indexOf('}', css.indexOf(selector))); + expect(rule('.sa-e-stepper .estepper-divider {')).toContain('flex: 0 0 auto'); + expect(rule('.sa-e-stepper .estepper-divider.vertical')).toContain('flex: 0 0 auto'); // Above the arrows from the program (which are drawn over both panes), so they pass under it. - expect(rule(".sa-e-stepper .estepper-divider {")).toContain("z-index: 3"); + expect(rule('.sa-e-stepper .estepper-divider {')).toContain('z-index: 3'); // A stacked tab leaves the diagram room for no more than its least height. - expect(css).toContain(".estepper-main.narrow .estepper-diagram { min-height: 80px; }"); + expect(css).toContain('.estepper-main.narrow .estepper-diagram { min-height: 80px; }'); }); diff --git a/src/web/e-stepper/src/colors.ts b/src/web/e-stepper/src/colors.ts index d18b85e..2c96ab8 100644 --- a/src/web/e-stepper/src/colors.ts +++ b/src/web/e-stepper/src/colors.ts @@ -5,17 +5,17 @@ * background. */ -const GLOBAL_COLOR = "#c5cbd3"; +const GLOBAL_COLOR = '#c5cbd3'; const PALETTE = [ - "#4fc3f7", // light blue - "#ffb74d", // orange - "#81c784", // green - "#f06292", // pink - "#ba68c8", // purple - "#fff176", // yellow - "#4db6ac", // teal - "#e57373", // red + '#4fc3f7', // light blue + '#ffb74d', // orange + '#81c784', // green + '#f06292', // pink + '#ba68c8', // purple + '#fff176', // yellow + '#4db6ac', // teal + '#e57373', // red ]; /** The colour of the frame at `index` in a step's frame list (index 0 is the global frame). */ @@ -24,17 +24,17 @@ export function frameColor(index: number): string { } export const DiagramColors = { - text: "#ffffff", - dimText: "#8a9ba8", - stroke: "#ffffff", + text: '#ffffff', + dimText: '#8a9ba8', + stroke: '#ffffff', /** What is dead (frames and objects nothing refers to any more, and their arrows and data). */ - garbage: "#7f8b96", + garbage: '#7f8b96', garbageOpacity: 0.75, - lookup: "rgba(255, 213, 79, 0.35)", + lookup: 'rgba(255, 213, 79, 0.35)', /** * A hovered frame's or object's background: darker, as in the host's CSE diagram. Not a colour * of its own, since frames are colour-coded. */ - hoverBackground: "#000000", - background: "#1a2530", + hoverBackground: '#000000', + background: '#1a2530', } as const; diff --git a/src/web/e-stepper/src/index.ts b/src/web/e-stepper/src/index.ts index d365ef8..00f5b58 100644 --- a/src/web/e-stepper/src/index.ts +++ b/src/web/e-stepper/src/index.ts @@ -1,3 +1,3 @@ // The host's external-plugin loader imports the bundle and calls its default export as a factory // (see wrap.mjs), so the entry default-exports the plugin class. -export { EStepperHostPlugin as default } from "./EStepperHostPlugin"; +export { EStepperHostPlugin as default } from './EStepperHostPlugin'; diff --git a/src/web/e-stepper/src/layout.ts b/src/web/e-stepper/src/layout.ts index fab70d4..5a147e1 100644 --- a/src/web/e-stepper/src/layout.ts +++ b/src/web/e-stepper/src/layout.ts @@ -15,7 +15,7 @@ import type { EStepperFrame, EStepperHeapObject, EStepperValue, -} from "@sourceacademy/common-e-stepper"; +} from '@sourceacademy/common-e-stepper'; export const LayoutConfig = { charWidth: 7.8, @@ -73,7 +73,7 @@ export interface ObjectBox { height: number; } -export type ArrowKind = "parent" | "binding" | "environment" | "element"; +export type ArrowKind = 'parent' | 'binding' | 'environment' | 'element'; export interface ArrowSpec { key: string; @@ -99,23 +99,23 @@ export function textWidth(text: string): number { /** The text shown for a non-reference value in a frame or list cell. */ export function valueLabel(value: EStepperValue): string { switch (value.kind) { - case "primitive": + case 'primitive': return value.display; - case "builtin": + case 'builtin': return value.name; - case "unassigned": - return "—"; - case "ref": - return ""; + case 'unassigned': + return '—'; + case 'ref': + return ''; } } function valueWidth(value: EStepperValue): number { - return value.kind === "ref" ? 3 * C.dotRadius : textWidth(valueLabel(value)); + return value.kind === 'ref' ? 3 * C.dotRadius : textWidth(valueLabel(value)); } function objectSize(object: EStepperHeapObject): { width: number; height: number } { - if (object.kind === "function") { + if (object.kind === 'function') { return { width: 4 * C.functionRadius, height: 2 * C.functionRadius }; } return { width: Math.max(1, object.elements.length) * C.cellWidth, height: C.cellHeight }; @@ -160,7 +160,7 @@ export function layoutDiagram( // first frame (in order) that reaches it through its bindings and owned objects. const owner = new Map(); for (const o of heap) { - if (o.kind === "function" && frameById.has(o.envId)) owner.set(o.id, o.envId); + if (o.kind === 'function' && frameById.has(o.envId)) owner.set(o.id, o.envId); } const ownedOrder = new Map(frames.map(f => [f.id, []])); const claim = (objectId: string, frameId: string): void => { @@ -171,13 +171,13 @@ export function layoutDiagram( if (ownedOrder.get(frameId)!.includes(objectId)) return; owner.set(objectId, frameId); ownedOrder.get(frameId)!.push(objectId); - if (object.kind === "list") { - for (const e of object.elements) if (e.kind === "ref") claim(e.objectId, frameId); + if (object.kind === 'list') { + for (const e of object.elements) if (e.kind === 'ref') claim(e.objectId, frameId); } }; for (const f of frames) { - for (const o of heap) if (o.kind === "function" && o.envId === f.id) claim(o.id, f.id); - for (const b of f.bindings) if (b.value.kind === "ref") claim(b.value.objectId, f.id); + for (const o of heap) if (o.kind === 'function' && o.envId === f.id) claim(o.id, f.id); + for (const b of f.bindings) if (b.value.kind === 'ref') claim(b.value.objectId, f.id); } const unowned = heap.filter(o => !owner.has(o.id)); @@ -194,7 +194,7 @@ export function layoutDiagram( for (const frame of row) { const contentWidth = Math.max( C.frameMinWidth - 2 * C.padding, - ...frame.bindings.map(b => textWidth(b.name + ":") + C.padding + valueWidth(b.value)), + ...frame.bindings.map(b => textWidth(b.name + ':') + C.padding + valueWidth(b.value)), textWidth(`${frame.id} ${frame.name}`) - C.padding, ); const frameWidth = contentWidth + 2 * C.padding; @@ -203,7 +203,7 @@ export function layoutDiagram( name: b.name, value: b.value, y: boxTop + C.padding / 2 + i * C.rowHeight + C.rowHeight / 2, - valueX: x + C.padding + textWidth(b.name + ":") + C.padding, + valueX: x + C.padding + textWidth(b.name + ':') + C.padding, })); const frameHeight = Math.max(C.rowHeight, frame.bindings.length * C.rowHeight) + C.padding; frameBoxes.push({ @@ -265,19 +265,19 @@ export function layoutDiagram( if (parent) { arrows.push({ key: `parent:${box.frame.id}`, - kind: "parent", + kind: 'parent', from: { x: box.x + box.width / 2, y: boxTop }, to: { x: parent.x + parent.width / 2, y: parent.y + C.headerHeight + parent.height }, garbage: box.frame.isGarbage, }); } for (const row of box.rows) { - if (row.value.kind !== "ref") continue; + if (row.value.kind !== 'ref') continue; const target = objectBoxById.get(row.value.objectId); if (!target) continue; arrows.push({ key: `binding:${box.frame.id}:${row.name}`, - kind: "binding", + kind: 'binding', from: { x: row.valueX + C.dotRadius, y: row.y }, to: arrowTarget(target), garbage: box.frame.isGarbage, @@ -286,24 +286,24 @@ export function layoutDiagram( } for (const box of objectBoxes) { const object = box.object; - if (object.kind === "function") { + if (object.kind === 'function') { const env = frameBoxById.get(object.envId); if (!env) continue; arrows.push({ key: `environment:${object.id}`, - kind: "environment", + kind: 'environment', from: { x: box.x + 3 * C.functionRadius, y: box.y + C.functionRadius }, to: { x: env.x + env.width, y: env.y + C.headerHeight + C.padding }, garbage: object.isGarbage, }); } else { object.elements.forEach((e, i) => { - if (e.kind !== "ref") return; + if (e.kind !== 'ref') return; const target = objectBoxById.get(e.objectId); if (!target) return; arrows.push({ key: `element:${object.id}:${i}`, - kind: "element", + kind: 'element', from: { x: box.x + (i + 0.5) * C.cellWidth, y: box.y + C.cellHeight / 2 }, to: arrowTarget(target), garbage: object.isGarbage, diff --git a/src/web/e-stepper/src/styles.ts b/src/web/e-stepper/src/styles.ts index 136b18f..7847a4b 100644 --- a/src/web/e-stepper/src/styles.ts +++ b/src/web/e-stepper/src/styles.ts @@ -59,14 +59,14 @@ const E_STEPPER_CSS = ` } `; -const STYLE_ELEMENT_ID = "sa-e-stepper-styles"; +const STYLE_ELEMENT_ID = 'sa-e-stepper-styles'; /** Injects (or refreshes) the e-stepper's stylesheet, like the stepper's own `injectStepperStyles`. */ export function injectEStepperStyles(): void { - if (typeof document === "undefined") return; + if (typeof document === 'undefined') return; let style = document.getElementById(STYLE_ELEMENT_ID) as HTMLStyleElement | null; if (style === null) { - style = document.createElement("style"); + style = document.createElement('style'); style.id = STYLE_ELEMENT_ID; document.head.appendChild(style); } diff --git a/src/web/e-stepper/wrap.mjs b/src/web/e-stepper/wrap.mjs index 13af2ae..4deb609 100644 --- a/src/web/e-stepper/wrap.mjs +++ b/src/web/e-stepper/wrap.mjs @@ -7,11 +7,11 @@ // in `export default require => { ...cjs...; return module.exports }`, shadowing `require`/`module`/ // `exports` so the bundle's external `require(...)` calls hit the provider. Output is written to // dist/index.mjs (the file the frontend serves & imports). -import { readFile, writeFile } from "node:fs/promises"; +import { readFile, writeFile } from 'node:fs/promises'; -const cjsUrl = new URL("./dist/index.cjs", import.meta.url); -const mjsUrl = new URL("./dist/index.mjs", import.meta.url); +const cjsUrl = new URL('./dist/index.cjs', import.meta.url); +const mjsUrl = new URL('./dist/index.mjs', import.meta.url); -const cjs = await readFile(cjsUrl, "utf-8"); +const cjs = await readFile(cjsUrl, 'utf-8'); const wrapped = `export default require => {let module = {exports: {}}; let exports = module.exports;\n${cjs}\n; return module.exports;}`; await writeFile(mjsUrl, wrapped); diff --git a/src/web/stepper/src/SubstVisualizer.tsx b/src/web/stepper/src/SubstVisualizer.tsx index e0a5dd0..cb1b107 100644 --- a/src/web/stepper/src/SubstVisualizer.tsx +++ b/src/web/stepper/src/SubstVisualizer.tsx @@ -1,12 +1,12 @@ -import { Button, ButtonGroup, Card, Classes, Divider, Pre, Slider } from "@blueprintjs/core"; -import { getHotkeyHandler, type HotkeyItem } from "@mantine/hooks"; -import type { SerializedStepperStep, SyntaxProfile } from "@sourceacademy/common-stepper"; -import classNames from "classnames"; -import { useCallback, useEffect, useState } from "react"; +import { Button, ButtonGroup, Card, Classes, Divider, Pre, Slider } from '@blueprintjs/core'; +import { getHotkeyHandler, type HotkeyItem } from '@mantine/hooks'; +import type { SerializedStepperStep, SyntaxProfile } from '@sourceacademy/common-stepper'; +import classNames from 'classnames'; +import { useCallback, useEffect, useState } from 'react'; -import { CustomASTRenderer } from "./render"; -import { sliderLabels } from "./sliderLabels"; -import { injectStepperStyles } from "./styles"; +import { CustomASTRenderer } from './render'; +import { sliderLabels } from './sliderLabels'; +import { injectStepperStyles } from './styles'; function SubstDefaultText() { return ( @@ -94,7 +94,7 @@ function useWidth(): [(element: HTMLElement | null) => void, number] { useEffect(() => { if (!element) return; setWidth(Math.floor(element.getBoundingClientRect().width)); - if (typeof ResizeObserver === "undefined") return; + if (typeof ResizeObserver === 'undefined') return; const observer = new ResizeObserver(entries => setWidth(Math.floor(entries[0].contentRect.width)), ); @@ -201,7 +201,7 @@ export default function StepperView(props: StepperViewProps) { return (
diff --git a/src/web/stepper/src/__tests__/SubstVisualizer.test.tsx b/src/web/stepper/src/__tests__/SubstVisualizer.test.tsx index d60c0ec..3d1de5a 100644 --- a/src/web/stepper/src/__tests__/SubstVisualizer.test.tsx +++ b/src/web/stepper/src/__tests__/SubstVisualizer.test.tsx @@ -2,14 +2,14 @@ * The stepper's step slider: it counts the steps taken, from 0 (the start) to the last, as in the * e-stepper and the CSE machine, and labels both ends. */ -import { Button, Slider } from "@blueprintjs/core"; -import type { SerializedStepperStep } from "@sourceacademy/common-stepper"; -import { act } from "react"; -import TestRenderer from "react-test-renderer"; -import { describe, expect, test, vi } from "vitest"; +import { Button, Slider } from '@blueprintjs/core'; +import type { SerializedStepperStep } from '@sourceacademy/common-stepper'; +import { act } from 'react'; +import TestRenderer from 'react-test-renderer'; +import { describe, expect, test, vi } from 'vitest'; -import StepperView from "../SubstVisualizer"; -import { sliderLabels } from "../sliderLabels"; +import StepperView from '../SubstVisualizer'; +import { sliderLabels } from '../sliderLabels'; // The hotkey library needs react-dom, which it does not declare (so it does not resolve here). // (Here it runs the binding for the key pressed, as the real one does.) @@ -19,7 +19,7 @@ vi.mock(import('@mantine/hooks'), () => ({ })); const step = (explanation: string): SerializedStepperStep => ({ - ast: { type: "Program", nodeId: "p", body: [] }, + ast: { type: 'Program', nodeId: 'p', body: [] }, markers: [{ explanation }], }); const run = (n: number) => Array.from({ length: n }, (_, i) => step(`step ${i}`)); @@ -32,8 +32,8 @@ function render(content: SerializedStepperStep[]) { return renderer; } -describe("the step slider", () => { - test("counts from 0 to the number of steps taken, and labels both ends", () => { +describe('the step slider', () => { + test('counts from 0 to the number of steps taken, and labels both ends', () => { const view = render(run(174)); const slider = () => view.root.findByType(Slider); expect(slider().props.min).toBe(0); @@ -45,23 +45,23 @@ describe("the step slider", () => { act(() => slider().props.onChange(173)); expect(slider().props.value).toBe(173); // The explanation shown is that of the 174th step, the last. - expect(JSON.stringify(view.toJSON())).toContain("step 173"); + expect(JSON.stringify(view.toJSON())).toContain('step 173'); }); - test("the buttons step, and the double arrows go to the first and the last step", () => { + test('the buttons step, and the double arrows go to the first and the last step', () => { const view = render(run(10)); const slider = () => view.root.findByType(Slider).props.value as number; const button = (icon: string) => view.root.findAllByType(Button).find(b => b.props.icon === icon)!; - act(() => button("chevron-right").props.onClick()); + act(() => button('chevron-right').props.onClick()); expect(slider()).toBe(1); - act(() => button("double-chevron-right").props.onClick()); + act(() => button('double-chevron-right').props.onClick()); expect(slider()).toBe(9); - act(() => button("double-chevron-left").props.onClick()); + act(() => button('double-chevron-left').props.onClick()); expect(slider()).toBe(0); }); - test("a run of one step gives the slider a valid range, disabled", () => { + test('a run of one step gives the slider a valid range, disabled', () => { const slider = render(run(1)).root.findByType(Slider); expect(slider.props.min).toBe(0); expect(slider.props.max).toBe(1); @@ -78,20 +78,20 @@ const withBreakpoints = (): SerializedStepperStep[] => ...s, markers: [ { - explanation: "Running breakpoint()", - redexType: "beforeMarker", - redexNodeType: "DebuggerStatement", + explanation: 'Running breakpoint()', + redexType: 'beforeMarker', + redexNodeType: 'DebuggerStatement', }, ], } : s, ); -describe("breakpoints and keys", () => { +describe('breakpoints and keys', () => { const press = (view: TestRenderer.ReactTestRenderer, key: string) => act(() => view.root - .find(n => typeof n.props.onKeyDown === "function") + .find(n => typeof n.props.onKeyDown === 'function') .props.onKeyDown({ key, preventDefault: () => {} }), ); const value = (view: TestRenderer.ReactTestRenderer) => @@ -99,71 +99,71 @@ describe("breakpoints and keys", () => { const button = (view: TestRenderer.ReactTestRenderer, icon: string) => view.root.findAllByType(Button).find(b => b.props.icon === icon)!; - test("the double arrows jump to the next and the previous breakpoint, else the last and first step", () => { + test('the double arrows jump to the next and the previous breakpoint, else the last and first step', () => { const view = render(withBreakpoints()); - act(() => button(view, "double-chevron-right").props.onClick()); + act(() => button(view, 'double-chevron-right').props.onClick()); expect(value(view)).toBe(3); - act(() => button(view, "double-chevron-right").props.onClick()); + act(() => button(view, 'double-chevron-right').props.onClick()); expect(value(view)).toBe(7); - act(() => button(view, "double-chevron-right").props.onClick()); + act(() => button(view, 'double-chevron-right').props.onClick()); expect(value(view)).toBe(9); - act(() => button(view, "double-chevron-left").props.onClick()); + act(() => button(view, 'double-chevron-left').props.onClick()); expect(value(view)).toBe(7); - act(() => button(view, "double-chevron-left").props.onClick()); + act(() => button(view, 'double-chevron-left').props.onClick()); expect(value(view)).toBe(3); - act(() => button(view, "double-chevron-left").props.onClick()); + act(() => button(view, 'double-chevron-left').props.onClick()); expect(value(view)).toBe(0); }); - test("f / b step, a / e jump between breakpoints", () => { + test('f / b step, a / e jump between breakpoints', () => { const view = render(withBreakpoints()); - press(view, "f"); - press(view, "f"); + press(view, 'f'); + press(view, 'f'); expect(value(view)).toBe(2); - press(view, "b"); + press(view, 'b'); expect(value(view)).toBe(1); - press(view, "e"); + press(view, 'e'); expect(value(view)).toBe(3); - press(view, "a"); + press(view, 'a'); expect(value(view)).toBe(0); // Not past either end. - press(view, "b"); + press(view, 'b'); expect(value(view)).toBe(0); - for (let i = 0; i < 12; i++) press(view, "f"); + for (let i = 0; i < 12; i++) press(view, 'f'); expect(value(view)).toBe(9); }); - test("a step without markers or an explanation says so with dots", () => { + test('a step without markers or an explanation says so with dots', () => { const view = render([ - { ast: { type: "Program", nodeId: "p", body: [] } }, - { ast: { type: "Program", nodeId: "p", body: [] }, markers: [{}] }, + { ast: { type: 'Program', nodeId: 'p', body: [] } }, + { ast: { type: 'Program', nodeId: 'p', body: [] }, markers: [{}] }, ]); const explanation = () => JSON.stringify(view.toJSON()); - expect(explanation()).toContain("..."); - act(() => button(view, "chevron-right").props.onClick()); - expect(explanation()).toContain("..."); + expect(explanation()).toContain('...'); + act(() => button(view, 'chevron-right').props.onClick()); + expect(explanation()).toContain('...'); }); - test("the output printed so far is shown with the step", () => { + test('the output printed so far is shown with the step', () => { const view = render([ - { ...step("a"), output: "" }, - { ...step("b"), output: "hello\n" }, + { ...step('a'), output: '' }, + { ...step('b'), output: 'hello\n' }, ]); - expect(JSON.stringify(view.toJSON())).not.toContain("hello"); - act(() => button(view, "chevron-right").props.onClick()); - expect(JSON.stringify(view.toJSON())).toContain("hello"); + expect(JSON.stringify(view.toJSON())).not.toContain('hello'); + act(() => button(view, 'chevron-right').props.onClick()); + expect(JSON.stringify(view.toJSON())).toContain('hello'); }); - test("before a run there is the welcome text, and the keys do nothing", () => { + test('before a run there is the welcome text, and the keys do nothing', () => { const view = render([]); - expect(JSON.stringify(view.toJSON())).toContain("Welcome to the Stepper!"); - for (const key of ["a", "f", "b", "e"]) press(view, key); + expect(JSON.stringify(view.toJSON())).toContain('Welcome to the Stepper!'); + for (const key of ['a', 'f', 'b', 'e']) press(view, key); expect(value(view)).toBe(0); }); }); describe(sliderLabels, () => { - test("labels 0, a round step apart, and always the last, never crowding it", () => { + test('labels 0, a round step apart, and always the last, never crowding it', () => { expect(sliderLabels(0)).toEqual([0]); expect(sliderLabels(1)).toEqual([0, 1]); expect(sliderLabels(10)).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); @@ -174,10 +174,10 @@ describe(sliderLabels, () => { }); describe("the step slider's track", () => { - test("is measured again when the tab is resized (a click maps to the right step)", () => { + test('is measured again when the tab is resized (a click maps to the right step)', () => { const observers: ((entries: { contentRect: { width: number } }[]) => void)[] = []; vi.stubGlobal( - "ResizeObserver", + 'ResizeObserver', class { constructor(callback: (typeof observers)[number]) { observers.push(callback); @@ -187,10 +187,10 @@ describe("the step slider's track", () => { }, ); // A Blueprint slider takes its listeners off the document when it goes away. - vi.stubGlobal("document", { + vi.stubGlobal('document', { addEventListener() {}, removeEventListener() {}, - getElementById: () => ({ textContent: "" }), + getElementById: () => ({ textContent: '' }), }); let view!: TestRenderer.ReactTestRenderer; act(() => { diff --git a/src/web/stepper/src/__tests__/render.test.tsx b/src/web/stepper/src/__tests__/render.test.tsx index b9caf25..7190862 100644 --- a/src/web/stepper/src/__tests__/render.test.tsx +++ b/src/web/stepper/src/__tests__/render.test.tsx @@ -3,13 +3,13 @@ * used when no syntax profile is given, the profile-driven renderer, function values, hover text, * redex markers, parenthesisation and host-specific node renderers. */ -import { Popover } from "@blueprintjs/core"; -import type { SerializedMarker, SyntaxProfile } from "@sourceacademy/common-stepper"; -import { act } from "react"; -import TestRenderer from "react-test-renderer"; -import { describe, expect, test } from "vitest"; +import { Popover } from '@blueprintjs/core'; +import type { SerializedMarker, SyntaxProfile } from '@sourceacademy/common-stepper'; +import { act } from 'react'; +import TestRenderer from 'react-test-renderer'; +import { describe, expect, test } from 'vitest'; -import { CustomASTRenderer, type StepperNode } from "../render"; +import { CustomASTRenderer, type StepperNode } from '../render'; let ids = 0; /** A node with a fresh nodeId. */ @@ -18,17 +18,17 @@ const n = (type: string, fields: Record = {}): StepperNode => ( nodeId: `n${ids++}`, ...fields, }); -const lit = (raw: string) => n("Literal", { raw, value: raw }); -const id = (name: string) => n("Identifier", { name }); +const lit = (raw: string) => n('Literal', { raw, value: raw }); +const id = (name: string) => n('Identifier', { name }); const bin = (operator: string, left: StepperNode, right: StepperNode) => - n("BinaryExpression", { operator, left, right }); + n('BinaryExpression', { operator, left, right }); function render( ast: StepperNode, options: { profile?: SyntaxProfile; markers?: SerializedMarker[]; - nodeRenderers?: Parameters[0]["nodeRenderers"]; + nodeRenderers?: Parameters[0]['nodeRenderers']; } = {}, ) { let renderer!: TestRenderer.ReactTestRenderer; @@ -40,215 +40,215 @@ function render( /** The rendered text (popover contents excluded: they render only when opened). */ const text = (node: TestRenderer.ReactTestInstance | string): string => - typeof node === "string" ? node : node.children.map(text).join(""); + typeof node === 'string' ? node : node.children.map(text).join(''); -describe("built-in (Source) renderers", () => { - test("a whole program", () => { - const fn = n("ArrowFunctionExpression", { - params: [id("x")], - body: bin("*", id("x"), id("x")), +describe('built-in (Source) renderers', () => { + test('a whole program', () => { + const fn = n('ArrowFunctionExpression', { + params: [id('x')], + body: bin('*', id('x'), id('x')), }); - const program = n("Program", { + const program = n('Program', { body: [ - n("VariableDeclaration", { - kind: "const", - declarations: [n("VariableDeclarator", { id: id("sq"), init: fn })], + n('VariableDeclaration', { + kind: 'const', + declarations: [n('VariableDeclarator', { id: id('sq'), init: fn })], }), - n("FunctionDeclaration", { - id: id("f"), - params: [id("a"), id("b")], - body: n("BlockStatement", { + n('FunctionDeclaration', { + id: id('f'), + params: [id('a'), id('b')], + body: n('BlockStatement', { body: [ - n("IfStatement", { - test: n("LogicalExpression", { operator: "&&", left: id("a"), right: id("b") }), - consequent: n("BlockStatement", { - body: [n("ReturnStatement", { argument: lit("1") })], + n('IfStatement', { + test: n('LogicalExpression', { operator: '&&', left: id('a'), right: id('b') }), + consequent: n('BlockStatement', { + body: [n('ReturnStatement', { argument: lit('1') })], }), - alternate: n("BlockStatement", { - body: [n("ReturnStatement", { argument: null })], + alternate: n('BlockStatement', { + body: [n('ReturnStatement', { argument: null })], }), }), ], }), }), - n("ExpressionStatement", { - expression: n("CallExpression", { + n('ExpressionStatement', { + expression: n('CallExpression', { callee: fn, - arguments: [n("UnaryExpression", { operator: "-", argument: lit("2") })], + arguments: [n('UnaryExpression', { operator: '-', argument: lit('2') })], }), }), - n("ExpressionStatement", { - expression: n("ConditionalExpression", { - test: id("c"), - consequent: n("ArrayExpression", { elements: [lit("1"), lit("2"), null] }), - alternate: n("Literal", { value: "s" }), + n('ExpressionStatement', { + expression: n('ConditionalExpression', { + test: id('c'), + consequent: n('ArrayExpression', { elements: [lit('1'), lit('2'), null] }), + alternate: n('Literal', { value: 's' }), }), }), - n("DebuggerStatement"), - n("ExpressionStatement", { expression: n("Literal", { value: null }) }), - n("UnknownNode"), + n('DebuggerStatement'), + n('ExpressionStatement', { expression: n('Literal', { value: null }) }), + n('UnknownNode'), ], }); const all = text(render(program).root); - expect(all).toContain("const sq = x => x * x;"); - expect(all).toContain("function f(a, b)"); - expect(all).toContain("if (a && b)"); - expect(all).toContain("return 1;"); - expect(all).toContain(" else "); - expect(all).toContain("(x => x * x)(-2);"); + expect(all).toContain('const sq = x => x * x;'); + expect(all).toContain('function f(a, b)'); + expect(all).toContain('if (a && b)'); + expect(all).toContain('return 1;'); + expect(all).toContain(' else '); + expect(all).toContain('(x => x * x)(-2);'); expect(all).toContain('c ? [1, 2] : "s";'); - expect(all).toContain("debugger;"); - expect(all).toContain("null;"); - expect(all).toContain(""); + expect(all).toContain('debugger;'); + expect(all).toContain('null;'); + expect(all).toContain(''); }); - test("a named function value is a mu-term with its definition in a popover", () => { - const fn = n("ArrowFunctionExpression", { - name: "fact", - params: [id("n")], - body: n("CallExpression", { callee: id("fact"), arguments: [id("n")] }), + test('a named function value is a mu-term with its definition in a popover', () => { + const fn = n('ArrowFunctionExpression', { + name: 'fact', + params: [id('n')], + body: n('CallExpression', { callee: id('fact'), arguments: [id('n')] }), }); - const view = render(n("ExpressionStatement", { expression: fn })); - expect(text(view.root)).toBe("fact;"); + const view = render(n('ExpressionStatement', { expression: fn })); + expect(text(view.root)).toBe('fact;'); expect(view.root.findAllByType(Popover).length).toBeGreaterThan(0); }); - test("parentheses follow precedence and associativity", () => { - const sum = bin("+", lit("1"), lit("2")); - expect(text(render(bin("*", sum, lit("3"))).root)).toBe("(1 + 2) * 3"); - expect(text(render(bin("-", lit("1"), bin("-", lit("2"), lit("3")))).root)).toBe("1 - (2 - 3)"); - expect(text(render(bin("**", bin("**", lit("2"), lit("3")), lit("4"))).root)).toBe( - "(2 ** 3) ** 4", + test('parentheses follow precedence and associativity', () => { + const sum = bin('+', lit('1'), lit('2')); + expect(text(render(bin('*', sum, lit('3'))).root)).toBe('(1 + 2) * 3'); + expect(text(render(bin('-', lit('1'), bin('-', lit('2'), lit('3')))).root)).toBe('1 - (2 - 3)'); + expect(text(render(bin('**', bin('**', lit('2'), lit('3')), lit('4'))).root)).toBe( + '(2 ** 3) ** 4', ); - expect(text(render(bin("**", lit("2"), bin("**", lit("3"), lit("4")))).root)).toBe( - "2 ** 3 ** 4", + expect(text(render(bin('**', lit('2'), bin('**', lit('3'), lit('4')))).root)).toBe( + '2 ** 3 ** 4', ); expect( text( render( - n("LogicalExpression", { - operator: "??", - left: n("LogicalExpression", { operator: "||", left: id("a"), right: id("b") }), - right: id("c"), + n('LogicalExpression', { + operator: '??', + left: n('LogicalExpression', { operator: '||', left: id('a'), right: id('b') }), + right: id('c'), }), ).root, ), - ).toBe("(a || b) ?? c"); - expect(text(render(n("UnaryExpression", { operator: "-", argument: sum })).root)).toBe( - "-(1 + 2)", + ).toBe('(a || b) ?? c'); + expect(text(render(n('UnaryExpression', { operator: '-', argument: sum })).root)).toBe( + '-(1 + 2)', ); }); - test("redex markers wrap the marked node", () => { - const left = lit("1"); - const ast = bin("+", left, lit("2")); - const view = render(ast, { markers: [{ redexId: left.nodeId, redexType: "beforeMarker" }] }); - const marked = view.root.findAll(x => x.props.className === "beforeMarker"); + test('redex markers wrap the marked node', () => { + const left = lit('1'); + const ast = bin('+', left, lit('2')); + const view = render(ast, { markers: [{ redexId: left.nodeId, redexType: 'beforeMarker' }] }); + const marked = view.root.findAll(x => x.props.className === 'beforeMarker'); expect(marked).toHaveLength(1); - expect(text(marked[0])).toBe("1"); + expect(text(marked[0])).toBe('1'); }); }); -describe("profile-driven rendering", () => { +describe('profile-driven rendering', () => { const profile: SyntaxProfile = { templates: { - Program: [{ lines: "body" }], + Program: [{ lines: 'body' }], Def: [ - { token: "def ", cls: "identifier" }, - { prop: "id.name", cls: "identifier" }, - "(", - { list: "params", sep: ", " }, - "):", - { block: "body" }, + { token: 'def ', cls: 'identifier' }, + { prop: 'id.name', cls: 'identifier' }, + '(', + { list: 'params', sep: ', ' }, + '):', + { block: 'body' }, ], - Return: [{ token: "return ", cls: "operator" }, { child: "argument" }], - Call: [{ child: "callee" }, "(", { list: "arguments", sep: ", ", prefix: "" }, ")"], + Return: [{ token: 'return ', cls: 'operator' }, { child: 'argument' }], + Call: [{ child: 'callee' }, '(', { list: 'arguments', sep: ', ', prefix: '' }, ')'], If: [ - { token: "if ", cls: "identifier" }, - { child: "test" }, - ":", - { block: "body" }, - { when: "orelse", parts: [{ token: "else:", cls: "identifier" }, { block: "orelse" }] }, - { unless: "orelse", parts: ["# no else"] }, + { token: 'if ', cls: 'identifier' }, + { child: 'test' }, + ':', + { block: 'body' }, + { when: 'orelse', parts: [{ token: 'else:', cls: 'identifier' }, { block: 'orelse' }] }, + { unless: 'orelse', parts: ['# no else'] }, ], Bin: [ - { child: "left" }, - " ", - { prop: "operator", cls: "operator" }, - " ", - { child: "right", isRight: true }, + { child: 'left' }, + ' ', + { prop: 'operator', cls: 'operator' }, + ' ', + { child: 'right', isRight: true }, ], - Id: [{ prop: "name" }], - Num: [{ prop: "raw", cls: "literal" }], - Builtin: [{ prop: "name" }], + Id: [{ prop: 'name' }], + Num: [{ prop: 'raw', cls: 'literal' }], + Builtin: [{ prop: 'name' }], Img: [ - { image: "src", altProp: "label" }, - { unless: "src", parts: ["<", { prop: "label" }, ">"] }, + { image: 'src', altProp: 'label' }, + { unless: 'src', parts: ['<', { prop: 'label' }, '>'] }, ], }, - operatorPrecedence: { "+": 11, "*": 12 }, + operatorPrecedence: { '+': 11, '*': 12 }, expressionPrecedence: { Id: 20, Num: 18, Call: 18, Bin: 14 }, - functionValues: [{ type: "Def", nameProp: "name" }], - hoverText: [{ type: "Builtin", textProp: "hoverText" }], + functionValues: [{ type: 'Def', nameProp: 'name' }], + hoverText: [{ type: 'Builtin', textProp: 'hoverText' }], }; - const pid = (name: string) => n("Id", { name }); - const num = (raw: string) => n("Num", { raw }); + const pid = (name: string) => n('Id', { name }); + const num = (raw: string) => n('Num', { raw }); const pbin = (operator: string, left: StepperNode, right: StepperNode) => - n("Bin", { operator, left, right }); + n('Bin', { operator, left, right }); - test("templates: tokens, props, children, lists, blocks, lines, when/unless", () => { - const program = n("Program", { + test('templates: tokens, props, children, lists, blocks, lines, when/unless', () => { + const program = n('Program', { body: [ - n("Def", { - id: { name: "f" }, - params: [pid("x")], - body: [n("Return", { argument: pbin("*", pbin("+", pid("x"), num("1")), num("2")) })], + n('Def', { + id: { name: 'f' }, + params: [pid('x')], + body: [n('Return', { argument: pbin('*', pbin('+', pid('x'), num('1')), num('2')) })], }), - n("If", { - test: pid("c"), - body: [n("Call", { callee: pid("g"), arguments: [] })], - orelse: [num("0")], + n('If', { + test: pid('c'), + body: [n('Call', { callee: pid('g'), arguments: [] })], + orelse: [num('0')], }), - n("If", { test: pid("d"), body: [num("1")], orelse: null }), - n("Unmapped"), + n('If', { test: pid('d'), body: [num('1')], orelse: null }), + n('Unmapped'), ], }); const all = text(render(program, { profile }).root); - expect(all).toContain("def f(x):"); - expect(all).toContain("return (x + 1) * 2"); - expect(all).toContain("if c:g()else:0"); - expect(all).toContain("if d:1# no else"); - expect(all).toContain(""); + expect(all).toContain('def f(x):'); + expect(all).toContain('return (x + 1) * 2'); + expect(all).toContain('if c:g()else:0'); + expect(all).toContain('if d:1# no else'); + expect(all).toContain(''); }); - test("a named function value collapses to its name with a popover", () => { - const def = n("Def", { name: "f", id: { name: "f" }, params: [], body: [] }); - const view = render(n("Call", { callee: def, arguments: [] }), { profile }); - expect(text(view.root)).toBe("f()"); + test('a named function value collapses to its name with a popover', () => { + const def = n('Def', { name: 'f', id: { name: 'f' }, params: [], body: [] }); + const view = render(n('Call', { callee: def, arguments: [] }), { profile }); + expect(text(view.root)).toBe('f()'); expect(view.root.findAllByType(Popover).length).toBeGreaterThan(0); }); - test("hover text and inline images", () => { - const builtin = n("Builtin", { name: "print", hoverText: "built-in function print" }); + test('hover text and inline images', () => { + const builtin = n('Builtin', { name: 'print', hoverText: 'built-in function print' }); const view = render( - n("Call", { + n('Call', { callee: builtin, - arguments: [n("Img", { src: "data:image/png;base64,AAAA", label: "Rune" })], + arguments: [n('Img', { src: 'data:image/png;base64,AAAA', label: 'Rune' })], }), { profile }, ); expect(view.root.findAllByType(Popover).length).toBe(2); - expect(view.root.findAllByType("img")).toHaveLength(1); + expect(view.root.findAllByType('img')).toHaveLength(1); // Only data: URLs are shown as images; anything else falls back to the label. - const fallback = render(n("Img", { src: "https://example.com/x.png", label: "Rune" }), { + const fallback = render(n('Img', { src: 'https://example.com/x.png', label: 'Rune' }), { profile, }); - expect(fallback.root.findAllByType("img")).toHaveLength(0); - expect(text(render(n("Img", { label: "Rune" }), { profile }).root)).toBe(""); + expect(fallback.root.findAllByType('img')).toHaveLength(0); + expect(text(render(n('Img', { label: 'Rune' }), { profile }).root)).toBe(''); }); - test("host-specific node renderers take precedence and render children in context", () => { - const view = render(n("Block", { body: pbin("+", pid("a"), num("1")) }), { + test('host-specific node renderers take precedence and render children in context', () => { + const view = render(n('Block', { body: pbin('+', pid('a'), num('1')) }), { profile, nodeRenderers: { Block: (node, renderChild) => ( @@ -256,12 +256,12 @@ describe("profile-driven rendering", () => { ), }, }); - const block = view.root.find(x => x.props.className === "host-block"); - expect(text(block)).toBe("a + 1"); + const block = view.root.find(x => x.props.className === 'host-block'); + expect(text(block)).toBe('a + 1'); }); }); -describe("popover contents", () => { +describe('popover contents', () => { /** Renders the (lazily rendered) content of every popover in `view`, recursively. */ function popoverTexts(view: TestRenderer.ReactTestRenderer, depth = 2): string[] { if (depth === 0) return []; @@ -274,51 +274,51 @@ describe("popover contents", () => { }); } - test("a Source function value shows its definition, recursively", () => { - const fn = n("ArrowFunctionExpression", { - name: "fact", - params: [id("n")], - body: n("CallExpression", { callee: id("fact"), arguments: [id("n")] }), + test('a Source function value shows its definition, recursively', () => { + const fn = n('ArrowFunctionExpression', { + name: 'fact', + params: [id('n')], + body: n('CallExpression', { callee: id('fact'), arguments: [id('n')] }), }); - const texts = popoverTexts(render(n("ExpressionStatement", { expression: fn }))); - expect(texts[0]).toContain("Function definition"); - expect(texts[0]).toContain("n => fact(n)"); + const texts = popoverTexts(render(n('ExpressionStatement', { expression: fn }))); + expect(texts[0]).toContain('Function definition'); + expect(texts[0]).toContain('n => fact(n)'); // The recursive reference inside the definition is itself a mu-term with the same popover. - expect(texts.filter(t => t.includes("n => fact(n)")).length).toBeGreaterThan(1); + expect(texts.filter(t => t.includes('n => fact(n)')).length).toBeGreaterThan(1); }); - test("an anonymous Source lambda refers to nothing by name, so it renders inline", () => { - const fn = n("ArrowFunctionExpression", { params: [id("a"), id("b")], body: id("a") }); - expect(text(render(fn).root)).toBe("(a, b) => a"); + test('an anonymous Source lambda refers to nothing by name, so it renders inline', () => { + const fn = n('ArrowFunctionExpression', { params: [id('a'), id('b')], body: id('a') }); + expect(text(render(fn).root)).toBe('(a, b) => a'); }); - test("a profile function value, hover text and an image show their popovers", () => { + test('a profile function value, hover text and an image show their popovers', () => { const profile: SyntaxProfile = { templates: { - Def: ["def ", { prop: "id.name" }, "(): ", { child: "body" }], - Call: [{ child: "callee" }, "(", { list: "arguments", sep: ", " }, ")"], - Id: [{ prop: "name" }], - Builtin: [{ prop: "name" }], - Img: [{ image: "src", altProp: "label" }], + Def: ['def ', { prop: 'id.name' }, '(): ', { child: 'body' }], + Call: [{ child: 'callee' }, '(', { list: 'arguments', sep: ', ' }, ')'], + Id: [{ prop: 'name' }], + Builtin: [{ prop: 'name' }], + Img: [{ image: 'src', altProp: 'label' }], }, - functionValues: [{ type: "Def", nameProp: "name" }], - hoverText: [{ type: "Builtin", textProp: "hoverText" }], + functionValues: [{ type: 'Def', nameProp: 'name' }], + hoverText: [{ type: 'Builtin', textProp: 'hoverText' }], }; - const def = n("Def", { - name: "f", - id: { name: "f" }, - body: n("Call", { callee: n("Id", { name: "f" }), arguments: [] }), + const def = n('Def', { + name: 'f', + id: { name: 'f' }, + body: n('Call', { callee: n('Id', { name: 'f' }), arguments: [] }), }); - const ast = n("Call", { - callee: n("Builtin", { name: "print", hoverText: "built-in function print" }), - arguments: [def, n("Img", { src: "data:image/png;base64,AAAA", label: "Rune" })], + const ast = n('Call', { + callee: n('Builtin', { name: 'print', hoverText: 'built-in function print' }), + arguments: [def, n('Img', { src: 'data:image/png;base64,AAAA', label: 'Rune' })], }); const texts = popoverTexts(render(ast, { profile })); expect(texts).toEqual( expect.arrayContaining([ - " built-in function print", - expect.stringContaining("def f(): f()"), - " Rune", + ' built-in function print', + expect.stringContaining('def f(): f()'), + ' Rune', ]), ); }); diff --git a/src/web/stepper/src/render.tsx b/src/web/stepper/src/render.tsx index 2794134..6f5e63a 100644 --- a/src/web/stepper/src/render.tsx +++ b/src/web/stepper/src/render.tsx @@ -1,20 +1,20 @@ -import { Classes, Icon, Popover } from "@blueprintjs/core"; +import { Classes, Icon, Popover } from '@blueprintjs/core'; import type { FunctionValueRule, SerializedStepperNode, SerializedStepperStep, SyntaxProfile, SyntaxTemplatePart, -} from "@sourceacademy/common-stepper"; -import classNames from "classnames"; -import { useCallback } from "react"; +} from '@sourceacademy/common-stepper'; +import classNames from 'classnames'; +import { useCallback } from 'react'; /** * The serialized AST nodes are plain JSON. `Record` lets the renderer read the * language-specific fields (e.g. `left`, `operator`, `params`) without per-node typing, exactly as * the original (class-based) renderer did after casting. */ - + export type StepperNode = SerializedStepperNode & Record; /** @@ -61,16 +61,16 @@ interface RenderContext { /** Maps a profile token class to the stepper's CSS colour class. */ const TOKEN_CLASS: Record = { - operator: "stepper-operator", - identifier: "stepper-identifier", - literal: "stepper-literal", - conditional: "stepper-conditional-operator", + operator: 'stepper-operator', + identifier: 'stepper-identifier', + literal: 'stepper-literal', + conditional: 'stepper-conditional-operator', }; /** Reads a node property by a (possibly dotted, e.g. `"id.name"`) path, for profile `prop` parts. */ function readNodeProp(node: StepperNode, path: string): unknown { return path - .split(".") + .split('.') .reduce( (value, key) => (value == null ? value : (value as Record)[key]), node, @@ -117,14 +117,14 @@ function FunctionDefinitionPopoverContent({ renderFunctionArguments, }: FunctionDefinitionPopoverContentProps) { return ( -
+
- {" Function definition"} + {' Function definition'}
           
             {renderFunctionArguments(node.params, renderNode, styleWrapper, popoverDepth)}
-            {" => "}
+            {' => '}
             {renderNode(node.body, {
               styleWrapper: styleWrapper ?? (_node => p => p),
               popoverDepth: popoverDepth + 1,
@@ -157,10 +157,10 @@ function ProfileFunctionDefinitionPopover({
   profile,
 }: ProfileFunctionDefinitionPopoverProps) {
   return (
-    
+
- {" Function definition"} + {' Function definition'}
           
             {renderNode(node, {
@@ -184,7 +184,7 @@ function ProfileFunctionDefinitionPopover({
  */
 function ProfileHoverTextPopover({ text }: { text: string }) {
   return (
-    
+
{` ${text}`} @@ -196,7 +196,7 @@ function ProfileHoverTextPopover({ text }: { text: string }) { /** Popover body for an `image` part: the inline opaque-value thumbnail, enlarged to be legible. */ function EnlargedThumbnailPopover({ src, alt }: { src: string; alt?: string }) { return ( -
+
{alt ? ( <> @@ -204,7 +204,7 @@ function EnlargedThumbnailPopover({ src, alt }: { src: string; alt?: string }) { {` ${alt}`} ) : null} - {alt + {alt
); @@ -223,7 +223,7 @@ function renderNode( const renderers = { Literal(node: StepperNode) { const stringifyLiteralValue = (value: unknown) => - typeof value === "string" ? '"' + value + '"' : value !== null ? String(value) : "null"; + typeof value === 'string' ? '"' + value + '"' : value !== null ? String(value) : 'null'; return ( {node.raw ? node.raw : stringifyLiteralValue(node.value)} @@ -307,7 +307,7 @@ function renderNode( (result, item) => ( {result} - {", "} + {', '} {item} ), @@ -315,9 +315,9 @@ function renderNode( ); return ( - {"["} + {'['} {renderedArguments} - {"]"} + {']'} ); }, @@ -327,7 +327,7 @@ function renderNode( * corresponding to the name of lambda expression */ function muTermStyleWrapper(targetNode: StepperNode) { - if (targetNode.type === "Identifier" && targetNode.name === node.name) { + if (targetNode.type === 'Identifier' && targetNode.name === node.name) { function addHovering(preprocessed: React.ReactNode): React.ReactNode { return ( @@ -383,7 +383,7 @@ function renderNode( ) : ( {renderFunctionArguments(node.params, renderNode, styleWrapper, popoverDepth)} - {" => "} + {' => '} {renderNode(node.body, { styleWrapper: composeStyleWrapper(styleWrapper, muTermStyleWrapper)!, popoverDepth: popoverDepth, @@ -396,12 +396,12 @@ function renderNode( styleWrapper: styleWrapper, popoverDepth: popoverDepth, }); - if (node.callee.type === "ArrowFunctionExpression" && node.callee.name === undefined) { + if (node.callee.type === 'ArrowFunctionExpression' && node.callee.name === undefined) { renderedCallee = ( - {"("} + {'('} {renderedCallee} - {")"} + {')'} ); } @@ -427,12 +427,12 @@ function renderNode( return ( - {"if "} - {"("} + {'if '} + {'('} {renderNode(node.test, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} - {") "} + {') '} {renderNode(node.consequent, { @@ -442,7 +442,7 @@ function renderNode( {node.alternate && ( - {" else "} + {' else '} {renderNode(node.alternate, { styleWrapper: styleWrapper, popoverDepth: popoverDepth, @@ -455,23 +455,23 @@ function renderNode( ReturnStatement(node: StepperNode) { return ( - {"return "} + {'return '} {node.argument && renderNode(node.argument, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} - {";"} + {';'} ); }, BlockStatement(node: StepperNode) { return ( - {"{"} + {'{'} {node.body.map((ast: StepperNode, index: number) => ( -
+
{renderNode(ast, { styleWrapper, popoverDepth: popoverDepth })}
))} - {"}"} + {'}'} ); }, @@ -479,7 +479,7 @@ function renderNode( return ( {renderNode(node.expression, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} - {";"} + {';'} ); }, @@ -489,7 +489,7 @@ function renderNode( {`function ${node.id.name}`} {renderArguments(node.params)} - {" "} + {' '} {renderNode(node.body, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} @@ -501,11 +501,11 @@ function renderNode( {node.kind} {node.declarations.map((ast: StepperNode, idx: number) => ( - {idx !== 0 && ", "} + {idx !== 0 && ', '} {renderNode(ast, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} ))} - {";"} + {';'} ); }, @@ -513,10 +513,10 @@ function renderNode( return ( {renderNode(node.id, { styleWrapper: styleWrapper, popoverDepth: popoverDepth })} - {" = "} + {' = '} {node.init ? renderNode(node.init, { styleWrapper: styleWrapper, popoverDepth: popoverDepth }) - : "undefined"} + : 'undefined'} ); }, @@ -532,7 +532,7 @@ function renderNode( styleWrapper: StyleWrapper | undefined, popoverDepth: number, ) => { - if (!nodes) return "()"; + if (!nodes) return '()'; const args: React.ReactNode[] = nodes.map(arg => renderNodeFn(arg, { styleWrapper: styleWrapper ?? (_node => p => p), @@ -543,7 +543,7 @@ function renderNode( (result, item) => ( {result} - {", "} + {', '} {item} ), @@ -552,9 +552,9 @@ function renderNode( if (args.length !== 1) { renderedArguments = ( - {"("} + {'('} {renderedArguments} - {")"} + {')'} ); } @@ -562,7 +562,7 @@ function renderNode( }; const renderArguments = (nodes: StepperNode[] | undefined) => { - if (!nodes) return "()"; + if (!nodes) return '()'; const args: React.ReactNode[] = nodes.map(arg => renderNode(arg, { styleWrapper: styleWrapper, popoverDepth: popoverDepth }), ); @@ -570,7 +570,7 @@ function renderNode( (result, item) => ( {result} - {", "} + {', '} {item} ), @@ -578,9 +578,9 @@ function renderNode( ); renderedArguments = ( - {"("} + {'('} {renderedArguments} - {")"} + {')'} ); return renderedArguments; @@ -600,22 +600,22 @@ function renderNode( }); const cls = (c?: string) => (c ? TOKEN_CLASS[c] : undefined); const renderPart = (part: SyntaxTemplatePart, key: number): React.ReactNode => { - if (typeof part === "string") return {part}; - if ("token" in part) + if (typeof part === 'string') return {part}; + if ('token' in part) return ( {part.token} ); - if ("prop" in part) { + if ('prop' in part) { const value = readNodeProp(node, part.prop); return ( - {value == null ? "" : String(value)} + {value == null ? '' : String(value)} ); } - if ("child" in part) { + if ('child' in part) { const child = node[part.child] as StepperNode | null | undefined; return ( @@ -623,7 +623,7 @@ function renderNode( ); } - if ("list" in part) { + if ('list' in part) { const items = (node[part.list] as StepperNode[] | undefined) ?? []; if (items.length === 0) return null; return ( @@ -638,19 +638,19 @@ function renderNode( ); } - if ("block" in part) { + if ('block' in part) { const items = (node[part.block] as StepperNode[] | undefined) ?? []; return ( {items.map((item, i) => ( -
+
{renderNode(item, childContext({}))}
))} ); } - if ("lines" in part) { + if ('lines' in part) { const items = (node[part.lines] as StepperNode[] | undefined) ?? []; return ( @@ -660,21 +660,21 @@ function renderNode( ); } - if ("when" in part) { + if ('when' in part) { return node[part.when] ? ( {part.parts.map((p, i) => renderPart(p, i))} ) : null; } - if ("unless" in part) { + if ('unless' in part) { return node[part.unless] ? null : ( {part.parts.map((p, i) => renderPart(p, i))} ); } - if ("image" in part) { + if ('image' in part) { const src = readNodeProp(node, part.image); - if (typeof src !== "string" || !src.startsWith("data:")) return null; + if (typeof src !== 'string' || !src.startsWith('data:')) return null; const alt = part.altProp === undefined ? undefined : readNodeProp(node, part.altProp); - const altText = alt == null ? "" : String(alt); + const altText = alt == null ? '' : String(alt); return ( } > {altText} @@ -712,7 +712,7 @@ function renderNode( // Recursive hovering: identifiers in the body that refer back to this function (by name) are // themselves wrapped in the same popover, so a recursive definition stays explorable. const muTermWrapper: StyleWrapper = (targetNode: StepperNode) => - targetNode.type === "Identifier" && targetNode.name === funcName + targetNode.type === 'Identifier' && targetNode.name === funcName ? (preprocessed: React.ReactNode) => ( rule.type === currentNode.type); const hoverText = hoverRule ? readNodeProp(currentNode, hoverRule.textProp) : undefined; - if (typeof hoverText === "string" && hoverText !== "") { + if (typeof hoverText === 'string' && hoverText !== '') { const content = result; result = ( - {"("} + {'('} {result} - {")"} + {')'} ); } @@ -855,7 +855,7 @@ export function CustomASTRenderer( // Match by stable node id rather than object identity, which does not survive // serialization across the runner/host channel. if (marker.redexId !== undefined && marker.redexId === node.nodeId) { - const Wrapper = node.type === "FunctionDeclaration" ? "div" : "span"; + const Wrapper = node.type === 'FunctionDeclaration' ? 'div' : 'span'; returnNode = {returnNode}; } }); @@ -891,12 +891,14 @@ function expressionNeedsParenthesis( const opPrecedence = profile?.operatorPrecedence ?? OPERATOR_PRECEDENCE; const nodePrecedence = exprPrecedence[node.type as keyof typeof exprPrecedence] as - number | undefined; + | number + | undefined; if (nodePrecedence === NEEDS_PARENTHESES) { return true; } const parentNodePrecedence = exprPrecedence[parentNode.type as keyof typeof exprPrecedence] as - number | undefined; + | number + | undefined; if (nodePrecedence === undefined || parentNodePrecedence === undefined) { return false; } @@ -908,7 +910,7 @@ function expressionNeedsParenthesis( ); } - if (!("operator" in node) || !("operator" in parentNode)) { + if (!('operator' in node) || !('operator' in parentNode)) { return false; } @@ -916,14 +918,14 @@ function expressionNeedsParenthesis( // Not a `LogicalExpression` or `BinaryExpression` return false; } - if (node.operator === "**" && parentNode.operator === "**") { + if (node.operator === '**' && parentNode.operator === '**') { // Exponentiation operator has right-to-left associativity return !isRightHand; } if ( nodePrecedence === 13 && parentNodePrecedence === 13 && - (node.operator === "??" || parentNode.operator === "??") + (node.operator === '??' || parentNode.operator === '??') ) { return true; } @@ -936,31 +938,31 @@ function expressionNeedsParenthesis( : nodeOperatorPrecedence <= parentNodeOperatorPrecedence; } const OPERATOR_PRECEDENCE = { - "||": 2, - "??": 3, - "&&": 4, - "|": 5, - "^": 6, - "&": 7, - "==": 8, - "!=": 8, - "===": 8, - "!==": 8, - "<": 9, - ">": 9, - "<=": 9, - ">=": 9, + '||': 2, + '??': 3, + '&&': 4, + '|': 5, + '^': 6, + '&': 7, + '==': 8, + '!=': 8, + '===': 8, + '!==': 8, + '<': 9, + '>': 9, + '<=': 9, + '>=': 9, in: 9, instanceof: 9, - "<<": 10, - ">>": 10, - ">>>": 10, - "+": 11, - "-": 11, - "*": 12, - "%": 12, - "/": 12, - "**": 13, + '<<': 10, + '>>': 10, + '>>>': 10, + '+': 11, + '-': 11, + '*': 12, + '%': 12, + '/': 12, + '**': 13, }; const NEEDS_PARENTHESES = 17; const EXPRESSIONS_PRECEDENCE = { From 9d4d3056cb624cfa59415c84398643d4cbe1785d Mon Sep 17 00:00:00 2001 From: "DESKTOP-G08HS3B\\Lee Yi" Date: Wed, 21 Oct 2026 20:03:00 +0800 Subject: [PATCH 29/29] run format --- src/web/e-stepper/src/__tests__/EStepperView.test.tsx | 8 ++++---- src/web/e-stepper/src/__tests__/EnvDiagram.test.tsx | 4 ++-- src/web/e-stepper/src/__tests__/layout.test.ts | 8 ++++---- src/web/e-stepper/src/colors.ts | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/web/e-stepper/src/__tests__/EStepperView.test.tsx b/src/web/e-stepper/src/__tests__/EStepperView.test.tsx index b9c6ec1..b264c49 100644 --- a/src/web/e-stepper/src/__tests__/EStepperView.test.tsx +++ b/src/web/e-stepper/src/__tests__/EStepperView.test.tsx @@ -585,7 +585,7 @@ describe(EStepperView, () => { expect(view.root.findByType(ProgramArrows).props.all).toBe(false); }); - test("Clear Unreachable Objects is also available when only a heap object is unreachable", () => { + test('Clear Unreachable Objects is also available when only a heap object is unreachable', () => { const onlyObject: EStepperStep = { ...fixture[2], frames: fixture[2].frames.map(f => ({ ...f, isGarbage: false })), @@ -594,15 +594,15 @@ describe(EStepperView, () => { const view = render({ steps: [onlyObject], profile }); const clear = view.root .findAllByType(Button) - .find(b => b.props.text === "Clear Unreachable Objects")!; + .find(b => b.props.text === 'Clear Unreachable Objects')!; expect(clear.props.disabled).toBe(false); }); - test("Clear Unreachable Objects clears them from the diagram until the step changes", () => { + test('Clear Unreachable Objects clears them from the diagram until the step changes', () => { const view = render({ steps: fixture, profile }); const diagram = () => view.root.find(n => n.props.onAnchors && n.props.frames); const clear = () => - view.root.findAllByType(Button).find(b => b.props.text === "Clear Unreachable Objects")!; + view.root.findAllByType(Button).find(b => b.props.text === 'Clear Unreachable Objects')!; // Only the last step has unreachable frames (E2). expect(clear().props.disabled).toBe(true); act(() => diff --git a/src/web/e-stepper/src/__tests__/EnvDiagram.test.tsx b/src/web/e-stepper/src/__tests__/EnvDiagram.test.tsx index 70c9d04..2c62d22 100644 --- a/src/web/e-stepper/src/__tests__/EnvDiagram.test.tsx +++ b/src/web/e-stepper/src/__tests__/EnvDiagram.test.tsx @@ -105,7 +105,7 @@ describe(EnvDiagram, () => { expect(onHover).toHaveBeenLastCalledWith(null); }); - test("draws unreachable frames, their data and their arrows grey, and live ones in their colours", () => { + test('draws unreachable frames, their data and their arrows grey, and live ones in their colours', () => { const last = (steps as unknown as EStepperStep[])[2]; const { root } = draw({ frames: last.frames, heap: last.heap, activeFrameId: 'Global' }); const outlines = root @@ -121,7 +121,7 @@ describe(EnvDiagram, () => { expect(arrows).toContain(DiagramColors.stroke); }); - test("draws the values in a unreachable frame grey too, not just its names", () => { + test('draws the values in a unreachable frame grey too, not just its names', () => { const frame = (id: string, parentId: string | null, isGarbage: boolean) => ({ id, name: id === 'Global' ? 'global' : 'f', diff --git a/src/web/e-stepper/src/__tests__/layout.test.ts b/src/web/e-stepper/src/__tests__/layout.test.ts index e9c5693..f9c876d 100644 --- a/src/web/e-stepper/src/__tests__/layout.test.ts +++ b/src/web/e-stepper/src/__tests__/layout.test.ts @@ -126,12 +126,12 @@ test('the global frame is neutral, other frames get distinct colours', () => { expect(new Set(colors).size).toBe(4); }); -describe("layoutDiagram with unreachable frames cleared", () => { +describe('layoutDiagram with unreachable frames cleared', () => { const full = layoutDiagram(last.frames, last.heap); const cleared = layoutDiagram(last.frames, last.heap, { clearUnreachable: true }); const deadObjects = last.heap.filter(o => o.isGarbage).map(o => o.id); - test("garbage frames and objects are left out, and the rest stays", () => { + test('garbage frames and objects are left out, and the rest stays', () => { const unreachable = last.frames.filter(f => f.isGarbage).map(f => f.id); expect(unreachable.length).toBeGreaterThan(0); expect(cleared.frames.map(b => b.frame.id)).toEqual( @@ -147,7 +147,7 @@ describe("layoutDiagram with unreachable frames cleared", () => { expect(cleared.arrows.every(a => !a.garbage)).toBe(true); }); - test("nothing changes when nothing is unreachable", () => { + test('nothing changes when nothing is unreachable', () => { const live = last.frames.map(f => ({ ...f, isGarbage: false })); const heap = last.heap.map(o => ({ ...o, isGarbage: false })); expect(layoutDiagram(live, heap, { clearUnreachable: true })).toEqual( @@ -155,7 +155,7 @@ describe("layoutDiagram with unreachable frames cleared", () => { ); }); - test("a live frame keeps its colour index when unreachable frames before it are left out", () => { + test('a live frame keeps its colour index when unreachable frames before it are left out', () => { const frame = (id: string, isGarbage: boolean) => ({ id, name: 'f', diff --git a/src/web/e-stepper/src/colors.ts b/src/web/e-stepper/src/colors.ts index deae265..a4a1dca 100644 --- a/src/web/e-stepper/src/colors.ts +++ b/src/web/e-stepper/src/colors.ts @@ -24,11 +24,11 @@ export function frameColor(index: number): string { } export const DiagramColors = { - text: "#ffffff", - dimText: "#8a9ba8", - stroke: "#ffffff", + text: '#ffffff', + dimText: '#8a9ba8', + stroke: '#ffffff', /** What is unreachable (frames and objects nothing refers to any more, and their arrows and data). */ - garbage: "#7f8b96", + garbage: '#7f8b96', garbageOpacity: 0.75, lookup: 'rgba(255, 213, 79, 0.35)', /**