Skip to content

Commit

Permalink
adding version support
Browse files Browse the repository at this point in the history
  • Loading branch information
mayarajan3 committed Jun 5, 2024
1 parent 586026e commit 58eaf2a
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 34 deletions.
50 changes: 29 additions & 21 deletions extensions/src/common/extension/decorators/blocks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type BlockUtility from "$scratch-vm/engine/block-utility";
import { TypedClassDecorator, TypedGetterDecorator, TypedMethodDecorator, TypedSetterDecorator } from ".";
import { BlockType } from "$common/types/enums";
import { BlockMetadata, ScratchArgument, Argument, NoArgsBlock } from "$common/types";
import { BlockMetadata, ScratchArgument, Argument, NoArgsBlock, Config } from "$common/types";
import { getImplementationName } from "../mixins/base/scratchInfo/index";
import { ExtensionInstance } from "..";
import { isFunction, isString, tryCreateBundleTimeEvent } from "$common/utils";
Expand Down Expand Up @@ -55,36 +55,44 @@ export const setAccessorPrefix = "__setter__";
* @returns A manipulated version of the original method that is
*/


export function block<
const This extends ExtensionInstance,
const Args extends any[],
const Return,
const Fn extends (...args: Args) => Return,
const TRemoveUtil extends any[] = Args extends [...infer R extends any[], BlockUtility] ? R : Args,
const TRemoveUtil extends any[] = Args extends [...infer R extends any[], BlockUtility] ? R : Args
>
(
blockInfoOrGetter: (BlockMetadata<(...args: TRemoveUtil) => Return> | ((this: This, self: This) => BlockMetadata<(...args: TRemoveUtil) => Return>))
): TypedMethodDecorator<This, Args, Return, (...args: Args) => Return> {
, versions?: Config): TypedMethodDecorator<This, Args, Return, (...args: Args) => Return> {

return function (this: This, target: (this: This, ...args: Args) => Return, context: ClassMethodDecoratorContext<This, Fn>) {
const opcode = target.name;
const internalFuncName = getImplementationName(opcode);
// could add check for if this block is meant for scratch
context.addInitializer(function () { this.pushBlock(opcode, blockInfoOrGetter, target) });

const isProbableAtBundleTime = !isFunction(blockInfoOrGetter);
if (isProbableAtBundleTime) {
const { type } = blockInfoOrGetter;
blockBundleEvent?.fire({
methodName: opcode,
args: extractArgs(blockInfoOrGetter).map(a => isString(a) ? a : a.type),
// is 'any' an issue? Likely!
returns: type === "command" ? "void" : type === "Boolean" ? "bool" : "any",
scratchType: blockInfoOrGetter.type
});
}

return (function () { return this[internalFuncName].call(this, ...arguments) });
var opcode = target.name;
const internalFuncName = getImplementationName(opcode);
// could add check for if this block is meant for scratch
if (versions) {
context.addInitializer(function () { this.pushBlock(opcode, blockInfoOrGetter, target, versions) });
} else {
context.addInitializer(function () { this.pushBlock(opcode, blockInfoOrGetter, target) });
}


const isProbableAtBundleTime = !isFunction(blockInfoOrGetter);
if (isProbableAtBundleTime) {
const { type } = blockInfoOrGetter;
blockBundleEvent?.fire({
methodName: opcode,
args: extractArgs(blockInfoOrGetter).map(a => isString(a) ? a : a.type),
// is 'any' an issue? Likely!
returns: type === "command" ? "void" : type === "Boolean" ? "bool" : "any",
scratchType: blockInfoOrGetter.type
});
}
return (function () { return this[internalFuncName].call(this, ...arguments) });



};
}

Expand Down
31 changes: 24 additions & 7 deletions extensions/src/common/extension/decorators/newBlocks.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { BlockMetadata, Argument, ReturnTypeByBlockType, ScratchBlockType, ToArguments } from "$common/types";
import { BlockMetadata, Config, Argument, ReturnTypeByBlockType, ScratchBlockType, ToArguments } from "$common/types";
import { block } from "$common/extension/decorators/blocks";
import { ExtensionInstance } from "..";
import { TypedMethodDecorator } from ".";
import type BlockUtilityWithID from "$scratch-vm/engine/block-utility";

export const scratch = {
reporter: makeDecorator("reporter"),
command: makeDecorator("command"),
}


const process = (type: ScratchBlockType, strings: TemplateStringsArray, ...args: any[]) => {
if (args.length === 0) return { type, text: strings[0], };
const text = (...placeholders: any[]) => strings.map((str, i) => `${str}${placeholders[i] ?? ""}`).join("");
if (args.length === 1) return { type, text, arg: args[0] };
return { type, text, args };
return { type, text, args };
}

export function makeDecorator<T extends ScratchBlockType>(type: T): TemplateEngine<T>["execute"] {
Expand All @@ -20,11 +25,26 @@ export function makeDecorator<T extends ScratchBlockType>(type: T): TemplateEngi
const input: any = typeof builderOrStrings == "function"
? (instance) => builderOrStrings(instance, process.bind(null, type))
: process(type, builderOrStrings, ...args);
return block(input)(target, context);
if (target.config) {
return block(input, target.config)(target, context);
} else {
return block(input)(target, context);
}

}
}
}

export function scratchVersions(config: Config) {
return function(
target,
context
) {
target.config = config;
return target;
}
};

namespace Utility {
export type TaggedTemplate<Args extends any[], Return> = (strings: TemplateStringsArray, ...args: Args) => Return;
}
Expand Down Expand Up @@ -68,7 +88,4 @@ interface TemplateEngine<TBlockType extends ScratchBlockType> {
}


export const scratch = {
reporter: makeDecorator("reporter"),
command: makeDecorator("command"),
}

18 changes: 13 additions & 5 deletions extensions/src/common/extension/mixins/base/scratchInfo/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { castToType } from "$common/cast";
import CustomArgumentManager from "$common/extension/mixins/configurable/customArguments/CustomArgumentManager";
import { ArgumentType, BlockType } from "$common/types/enums";
import { Config } from "$common/types"
import { BlockOperation, ValueOf, Menu, ExtensionMetadata, ExtensionBlockMetadata, ExtensionMenuMetadata, DynamicMenu, BlockMetadata, BlockUtilityWithID, } from "$common/types";
import { registerButtonCallback } from "$common/ui";
import { isString, typesafeCall, } from "$common/utils";
Expand All @@ -23,6 +24,7 @@ const nonBlockContextError = "Block method was not given a block utility, and th
const checkForBlockContext = (blockUtility: BlockUtilityWithID) => isBlockUtilityWithID(blockUtility) ? void 0 : console.error(nonBlockContextError);



/**
* Wraps a blocks operation so that the arguments passed from Scratch are first extracted and then passed as indices in a parameter array.
* @param _this What will be bound to the 'this' context of the underlying operation
Expand Down Expand Up @@ -70,7 +72,7 @@ export const wrapOperation = <T extends MinimalExtensionInstance>(
* @see https://www.typescriptlang.org/docs/handbook/mixins.html
*/
export default function (Ctor: CustomizableExtensionConstructor) {
type BlockEntry = { definition: BlockDefinition<ScratchExtension, BlockOperation>, operation: BlockOperation };
type BlockEntry = { definition: BlockDefinition<ScratchExtension, BlockOperation>, operation: BlockOperation, versions: Config };
type BlockMap = Map<string, BlockEntry>;
abstract class ScratchExtension extends Ctor {
private readonly blockMap: BlockMap = new Map();
Expand All @@ -84,9 +86,15 @@ export default function (Ctor: CustomizableExtensionConstructor) {
* @param definition
* @param operation
*/
pushBlock<Fn extends BlockOperation>(opcode: string, definition: BlockDefinition<any, Fn>, operation: BlockOperation) {
// add functions parameter here
pushBlock<Fn extends BlockOperation>(opcode: string, definition: BlockDefinition<any, Fn>, operation: BlockOperation, versions?: Config) {
if (this.blockMap.has(opcode)) throw new Error(`Attempt to push block with opcode ${opcode}, but it was already set. This is assumed to be a mistake.`)
this.blockMap.set(opcode, { definition, operation } as BlockEntry);
if (versions) {
this.blockMap.set(opcode, { definition, operation, versions} as BlockEntry);
} else {
this.blockMap.set(opcode, { definition, operation, versions: [] } as BlockEntry);
}

}

protected getInfo(): ExtensionMetadata {
Expand All @@ -103,7 +111,7 @@ export default function (Ctor: CustomizableExtensionConstructor) {

private convertToInfo(details: [opcode: string, entry: BlockEntry]) {
const [opcode, entry] = details;
const { definition, operation } = entry;
const { definition, operation, versions } = entry;

// Utilize explicit casting to appease test framework's typechecker
const block = isBlockGetter(definition)
Expand All @@ -119,7 +127,7 @@ export default function (Ctor: CustomizableExtensionConstructor) {
const displayText = convertToDisplayText(opcode, text, args);
const argumentsInfo = convertToArgumentInfo(opcode, args, menus);

const info: ExtensionBlockMetadata = { opcode, text: displayText, blockType: type, arguments: argumentsInfo };
const info: ExtensionBlockMetadata = { opcode, text: displayText, blockType: type, arguments: argumentsInfo, versions };

if (type === BlockType.Button) {
const buttonID = getButtonID(id, opcode);
Expand Down
10 changes: 9 additions & 1 deletion extensions/src/common/types/framework/blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,16 @@ export type ButtonBlock = () => InternalButtonKey;

export type BlockMetadata<
Fn extends BlockOperation,
TFunctions extends Array<(...args: any[]) => any> = [],
TParameters extends any[] = Parameters<Fn> extends [...infer R, BlockUtility] ? R : Parameters<Fn>
> = Type<ReturnType<Fn>> & Text<TParameters> & Arguments<TParameters>;
> = Type<ReturnType<Fn>> & Text<TParameters> & Arguments<TParameters> & {
optionalFunctions?: TFunctions;
};

export type ArgTransformer = (...args: any[]) => any[];
export type Config = {
[index: number]: ArgTransformer;
};

export type Block<TExt extends BaseGenericExtension, TOp extends BlockOperation> = BlockMetadata<TOp> & Operation<TExt, TOp>;

Expand Down
3 changes: 3 additions & 0 deletions extensions/src/common/types/legacy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ArgumentType, BlockType } from "./enums";
import { ValueOf } from "./utils";
import { Config } from "."

// Type definitions for scratch-vm (extension environment) 3.0
// Project: https://github.com/LLK/scratch-vm#readme
Expand Down Expand Up @@ -131,6 +132,8 @@ export interface ExtensionBlockMetadata {

/** Map of argument placeholder to metadata about each arg. */
arguments?: Record<string, ExtensionArgumentMetadata> | undefined;

versions?: Config;
}

/** All the metadata needed to register an argument for an extension block. */
Expand Down

0 comments on commit 58eaf2a

Please sign in to comment.