Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
2349f7a
:wrench: chore(vm): convret util to be ts
SimonShiki May 10, 2026
8b70f2e
:wrench: chore(vm): migrate some engine stuffs to typescript
SimonShiki May 10, 2026
41a4374
:bug: fix(vm): correct reference
SimonShiki May 10, 2026
0589c27
:wrench: chore(vm): migrate engine/variable
SimonShiki May 10, 2026
d05e248
:wrench: chore(vm): migrate engine/adapter
SimonShiki May 10, 2026
c6e7f99
:fire: chore(vm): remove mutation adapter
SimonShiki May 10, 2026
fe8ec7a
:wrench: chore(vm): migrate profiler
SimonShiki May 10, 2026
46b43f0
:rewind: revert: ":fire: chore(vm): remove mutation adapter"
SimonShiki May 10, 2026
d3ac561
:wrench: chore(vm): migrate mutation adapter
SimonShiki May 10, 2026
5f2bf1b
:bug: fix(vm): dts generation strategy
SimonShiki May 10, 2026
5a1aad5
:wrench: chore(vm): migrate some io adapters
SimonShiki May 10, 2026
9750345
:wrench: chore(vm): migrate dispatchers
SimonShiki May 10, 2026
9fda883
:bug: fix(vm): tests except worker-related issues
SimonShiki May 10, 2026
ea63bc7
:art: chore(vm): fix lint
SimonShiki May 10, 2026
906c505
:art: chore(vm): fix lint
SimonShiki May 10, 2026
a840dcb
:bug: fix(vm): run ts worker in same process
SimonShiki May 10, 2026
10999ac
:wrench: chore(vm): migrate scratch3_control category
SimonShiki May 11, 2026
6340767
:wrench: chore(vm): migrate some categories
SimonShiki May 11, 2026
9b7f81e
:wrench: chore(vm): migrate left categories
SimonShiki May 11, 2026
c4c2536
:wrench: chore(vm): migrate sprite.js
SimonShiki May 11, 2026
e3ad9ff
:wrench: chore(vm): migrate thread.js
SimonShiki May 11, 2026
7e4319d
:wrench: chore(block): migrate block-utility
SimonShiki May 11, 2026
e1d7786
:art: chore(vm): lint fix
SimonShiki May 11, 2026
a3a5175
:bug: fix: build and tests
SimonShiki May 11, 2026
989ff04
:bug: fix(vm): missing util mock
SimonShiki May 11, 2026
55635b9
:wrench: chore(vm): migrate sequencer
SimonShiki May 11, 2026
575b238
:beers: fix(vm): accidentally change clone logic
SimonShiki May 11, 2026
5fb5b0c
:bug: fix(vm): inconsistent behavior
SimonShiki May 11, 2026
0980b45
:wrench: chore(vm): export schema
SimonShiki May 11, 2026
94f99d7
:wrench: chore(vm): fix build
SimonShiki May 11, 2026
3da5bea
:art: chore(vm): missing semicolon
SimonShiki Aug 14, 2026
fb0f1d2
:wrench: chore(vm): add missing comments
SimonShiki Aug 17, 2026
276aa21
:wrench: chore(vm): extract type traits
SimonShiki Aug 17, 2026
3ee5d3a
:construction: merge: update with dev/3.2
SimonShiki Aug 17, 2026
380e972
:wrench: chore(vm): refine types
SimonShiki Aug 17, 2026
f319a1d
:wrench: chore(vm): resolve requested changes
SimonShiki Aug 24, 2026
f5d30d9
:wrench: chore(vm): assume block always exists in runtime cache
SimonShiki Aug 24, 2026
5b97b78
:wrench: chore(vm): revert rendered target related changes
SimonShiki Aug 24, 2026
f8cebf7
:wrench: chore(vm): add jsdoc for type traits
SimonShiki Aug 24, 2026
c8a4926
:wrench: chore(vm): revert rendered target related changes 2
SimonShiki Aug 26, 2026
e67d1ec
:wrench: chore(vm): don't trigger et hats if thread get retired
SimonShiki Aug 26, 2026
23013b2
:wrench: chore(vm): re-add original comments & keep code style
SimonShiki Aug 26, 2026
2d36060
:wrench: chore(vm): check thread status instead
SimonShiki Aug 26, 2026
22ea1aa
:wrench: chore(vm): use null assertions for safe paths
SimonShiki Aug 26, 2026
fc9bfad
:wrench: chore(vm): use null assertions for step sequencer
SimonShiki Aug 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/block/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,9 +234,11 @@ export * as callbackRegistry from './callback_registry';
export * as constants from './constants';
export * as scratchBlocksUtils from './utils';
export type * as proceduresSerializer from './serialization/procedures';
export type * as variableModel from './variable_model';

export {reportValue} from './report_value';
export {Colours} from './theme';
export {BlockDragEnd} from './events/block_drag_end';
export * as Theme from './theme';
export {glowStack} from './glow';

Expand Down
5 changes: 5 additions & 0 deletions packages/gui/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,8 @@ declare module 'react-tabs' {
export const Tab: React.ComponentType<TabProps>;
export const TabPanel: React.ComponentType<CommonProps>;
}

/**
* Compile-time injected clipcc global metadata
*/
declare const clipcc: { VERSION?: string };
2 changes: 1 addition & 1 deletion packages/render/src/RenderWebGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -1391,7 +1391,7 @@ class RenderWebGL extends EventEmitter {
* Return drawable pixel data and color at a given scratch position
* @param {int} scratchX The scratch x coordinate of the picking location.
* @param {int} scratchY The scratch y coordinate of the picking location.
* @returns {?ColorExtraction} Data about the picked color
* @returns Data about the picked color
*/
extractColorInScratchCoordinate (scratchX, scratchY) {
this._doExitDrawRegion();
Expand Down
6 changes: 4 additions & 2 deletions packages/vm/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
const clipccConfig = require('eslint-config-clipcc');
const clipccNode = require('eslint-config-clipcc/node');
const clipccES6 = require('eslint-config-clipcc/es6');
const clipccTS = require('eslint-config-clipcc/ts');
const globals = require('globals');

module.exports = [
...clipccConfig,
...clipccNode,
...clipccES6,
...clipccTS,
{
files: ['src/**/*.js'],
files: ['src/**/*.{js,ts}'],
languageOptions: {
globals: {
...globals.browser
Expand All @@ -28,7 +30,7 @@ module.exports = [
}
},
{
files: ['test/**/*.js'],
files: ['test/**/*.{js,ts}'],
languageOptions: {
globals: {
...globals.browser,
Expand Down
9 changes: 6 additions & 3 deletions packages/vm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
},
"exports": {
"types": "./dist/types/index.d.ts",
"webpack": "./src/index.js",
"webpack": "./src/index.ts",
"node": "./dist/node/scratch-vm.js",
"browser": "./dist/web/scratch-vm.min.js",
"default": "./src/index.js"
"default": "./src/index.ts"
},
"scripts": {
"build:types": "tsc --project ./tsconfig.dts.json",
"build:types": "tsc",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test types should also get resolved if they use ts.

"build": "pnpm run build:types && webpack --progress --color --bail",
"coverage": "jest --silent --coverage",
"i18n:src": "mkdirp translations/core && format-message extract --out-file translations/core/en.json src/extensions/**/index.js",
Expand Down Expand Up @@ -57,6 +57,8 @@
"@babel/eslint-parser": "7.28.6",
"@babel/preset-env": "7.29.2",
"@babel/preset-typescript": "^7.28.5",
"@types/atob": "^2.1.4",
"@types/btoa": "^1.2.5",
"@types/fastestsmallesttextencoderdecoder": "^1.0.2",
"@types/node": "^25.5.2",
"adm-zip": "0.4.11",
Expand All @@ -73,6 +75,7 @@
"clipcc-svg-renderer": "workspace:~",
"codingclip-worker-loader": "^3.0.10",
"copy-webpack-plugin": "^14.0.0",
"domhandler": "^5.0.3",
"eslint": "^9.39.2",
"eslint-config-clipcc": "workspace:*",
"format-message-cli": "6.2.4",
Expand Down
21 changes: 21 additions & 0 deletions packages/vm/src/blocks/category_prototype.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type BlockUtility from '../engine/block-utility';
import type {HatMetadata, MonitorBlockInfo} from '../engine/runtime';

export type BlockArgs = {
[argName: string]: any;
mutation?: Record<string, any>;
}

export type BlockFunction = (args: BlockArgs, util: BlockUtility) => any;

export interface CategoryPrototype {
/**
* Retrieve the block primitives implemented by this package.
* @returns Mapping of opcode to Function.
*/
getPrimitives(): Record<string, BlockFunction>;
getHats?(): Record<string, HatMetadata>;
getMonitored?(): Record<string, MonitorBlockInfo>;
getOrders?(): Record<string, (string | {execute: string})[]>;
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
import Cast from '../util/cast.js';
// @ts-nocheck
import Cast from '../util/cast';
import type {BlockArgs, CategoryPrototype} from './category_prototype';
import type Runtime from '../engine/runtime';
import type Target from '../engine/target';
import type BlockUtility from '../engine/block-utility';
import type {BaseExecutionContext} from '../engine/block-utility';

interface ControlExecutionContext extends BaseExecutionContext {
loopCounter?: number;
index?: number;
}

class Scratch3ControlBlocks {
constructor (runtime) {
class Scratch3ControlBlocks implements CategoryPrototype {
/**
* The "counter" block value. For compatibility with 2.0.
*/
private _counter = 0;
constructor (
/**
* The runtime instantiating this block package.
* @type {Runtime}
*/
this.runtime = runtime;

/**
* The "counter" block value. For compatibility with 2.0.
* @type {number}
*/
this._counter = 0;

public runtime: Runtime
) {
this.runtime.on('RUNTIME_DISPOSED', this.clearCounter.bind(this));
}

/**
* Retrieve the block primitives implemented by this package.
* @returns {Record<string, Function>} Mapping of opcode to Function.
* @returns Mapping of opcode to Function.
*/
getPrimitives () {
return {
Expand Down Expand Up @@ -50,7 +58,7 @@ class Scratch3ControlBlocks {
};
}

repeat (args, util) {
repeat (args: BlockArgs, util: BlockUtility) {
const times = Math.round(Cast.toNumber(args.TIMES));
// Initialize loop
if (typeof util.stackFrame.loopCounter === 'undefined') {
Expand All @@ -60,56 +68,56 @@ class Scratch3ControlBlocks {
// When the branch finishes, `repeat` will be executed again and
// the second branch will be taken, yielding for the rest of the frame.
// Decrease counter
util.stackFrame.loopCounter--;
(util.stackFrame as ControlExecutionContext).loopCounter!--;
// If we still have some left, start the branch.
if (util.stackFrame.loopCounter >= 0) {
if ((util.stackFrame as ControlExecutionContext).loopCounter! >= 0) {
util.startBranch(1, true);
}
}

repeatUntil (args, util) {
repeatUntil (args: BlockArgs, util: BlockUtility) {
const condition = Cast.toBoolean(args.CONDITION);
// If the condition is false (repeat UNTIL), start the branch.
if (!condition) {
util.startBranch(1, true);
}
}

repeatWhile (args, util) {
repeatWhile (args: BlockArgs, util: BlockUtility) {
const condition = Cast.toBoolean(args.CONDITION);
// If the condition is true (repeat WHILE), start the branch.
if (condition) {
util.startBranch(1, true);
}
}

forEach (args, util) {
forEach (args: BlockArgs, util: BlockUtility) {
const variable = util.target.lookupOrCreateVariable(
args.VARIABLE.id, args.VARIABLE.name);

if (typeof util.stackFrame.index === 'undefined') {
util.stackFrame.index = 0;
}

if (util.stackFrame.index < Number(args.VALUE)) {
util.stackFrame.index++;
variable.value = util.stackFrame.index;
if ((util.stackFrame as ControlExecutionContext).index! < Number(args.VALUE)) {
(util.stackFrame as ControlExecutionContext).index!++;
variable.value = (util.stackFrame as ControlExecutionContext).index;
util.startBranch(1, true);
}
}

waitUntil (args, util) {
waitUntil (args: BlockArgs, util: BlockUtility) {
const condition = Cast.toBoolean(args.CONDITION);
if (!condition) {
util.yield();
}
}

forever (args, util) {
forever (args: BlockArgs, util: BlockUtility) {
util.startBranch(1, true);
}

wait (args, util) {
wait (args: BlockArgs, util: BlockUtility) {
if (util.stackTimerNeedsInit()) {
const duration = Math.max(0, 1000 * Cast.toNumber(args.DURATION));

Expand All @@ -121,14 +129,14 @@ class Scratch3ControlBlocks {
}
}

if (args, util) {
if (args: BlockArgs, util: BlockUtility) {
const condition = Cast.toBoolean(args.CONDITION);
if (condition) {
util.startBranch(1, false);
}
}

ifElse (args, util) {
ifElse (args: BlockArgs, util: BlockUtility) {
const condition = Cast.toBoolean(args.CONDITION);
if (condition) {
util.startBranch(1, false);
Expand All @@ -137,7 +145,7 @@ class Scratch3ControlBlocks {
}
}

stop (args, util) {
stop (args: BlockArgs, util: BlockUtility) {
const option = args.STOP_OPTION;
if (option === 'all') {
util.stopAll();
Expand All @@ -149,12 +157,12 @@ class Scratch3ControlBlocks {
}
}

createClone (args, util) {
createClone (args: BlockArgs, util: BlockUtility) {
// Cast argument to string
args.CLONE_OPTION = Cast.toString(args.CLONE_OPTION);

// Set clone target
let cloneTarget;
let cloneTarget: Target | undefined;
if (args.CLONE_OPTION === '_myself_') {
cloneTarget = util.target;
} else {
Expand All @@ -174,7 +182,7 @@ class Scratch3ControlBlocks {
}
}

deleteClone (args, util) {
deleteClone (args: BlockArgs, util: BlockUtility) {
if (util.target.isOriginal) return;
this.runtime.disposeTarget(util.target);
this.runtime.stopForTarget(util.target);
Expand All @@ -192,7 +200,7 @@ class Scratch3ControlBlocks {
this._counter++;
}

allAtOnce (args, util) {
allAtOnce (args: BlockArgs, util: BlockUtility) {
// Since the "all at once" block is implemented for compatiblity with
// Scratch 2.0 projects, it behaves the same way it did in 2.0, which
// is to simply run the contained script (like "if 1 = 1").
Expand Down
Loading
Loading