Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(generators): Migrate JavaScript generators to TypeScript #7602

Merged
merged 8 commits into from
Oct 30, 2023
5 changes: 2 additions & 3 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

# Demos, scripts, misc
/node_modules/*
/generators/*
/demos/*
/appengine/*
/externs/*
Expand All @@ -27,5 +26,5 @@
CHANGELOG.md
PULL_REQUEST_TEMPLATE.md

# Don't bother formatting js blocks since we're getting rid of them
/blocks/*.js
# Don't bother formatting JavaScript files we're about to migrate:
/generators/**/*.js
11 changes: 9 additions & 2 deletions generators/javascript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,15 @@ export const javascriptGenerator = new JavascriptGenerator();

// Install per-block-type generator functions:
const generators: typeof javascriptGenerator.forBlock = {
...colour, ...lists, ...logic, ...loops, ...math,
...procedures, ...text, ...variables, ...variablesDynamic
...colour,
...lists,
...logic,
...loops,
...math,
...procedures,
...text,
...variables,
...variablesDynamic,
};
for (const name in generators) {
javascriptGenerator.forBlock[name] = generators[name];
Expand Down
65 changes: 40 additions & 25 deletions generators/javascript/colour.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,44 @@ import type {Block} from '../../core/block.js';
import type {JavascriptGenerator} from './javascript_generator.js';
import {Order} from './javascript_generator.js';


export function colour_picker(block: Block, generator: JavascriptGenerator): [string, Order] {
export function colour_picker(
block: Block,
generator: JavascriptGenerator,
): [string, Order] {
// Colour picker.
const code = generator.quote_(block.getFieldValue('COLOUR'));
return [code, Order.ATOMIC];
};
}

export function colour_random(block: Block, generator: JavascriptGenerator): [string, Order] {
export function colour_random(
block: Block,
generator: JavascriptGenerator,
): [string, Order] {
// Generate a random colour.
const functionName = generator.provideFunction_('colourRandom', `
const functionName = generator.provideFunction_(
'colourRandom',
`
function ${generator.FUNCTION_NAME_PLACEHOLDER_}() {
var num = Math.floor(Math.random() * Math.pow(2, 24));
return '#' + ('00000' + num.toString(16)).substr(-6);
}
`);
`,
);
const code = functionName + '()';
return [code, Order.FUNCTION_CALL];
};
}

export function colour_rgb(block: Block, generator: JavascriptGenerator): [string, Order] {
export function colour_rgb(
block: Block,
generator: JavascriptGenerator,
): [string, Order] {
// Compose a colour from RGB components expressed as percentages.
const red = generator.valueToCode(block, 'RED', Order.NONE) || 0;
const green =
generator.valueToCode(block, 'GREEN', Order.NONE) || 0;
const blue =
generator.valueToCode(block, 'BLUE', Order.NONE) || 0;
const functionName = generator.provideFunction_('colourRgb', `
const green = generator.valueToCode(block, 'GREEN', Order.NONE) || 0;
const blue = generator.valueToCode(block, 'BLUE', Order.NONE) || 0;
const functionName = generator.provideFunction_(
'colourRgb',
`
function ${generator.FUNCTION_NAME_PLACEHOLDER_}(r, g, b) {
r = Math.max(Math.min(Number(r), 100), 0) * 2.55;
g = Math.max(Math.min(Number(g), 100), 0) * 2.55;
Expand All @@ -50,20 +61,23 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(r, g, b) {
b = ('0' + (Math.round(b) || 0).toString(16)).slice(-2);
return '#' + r + g + b;
}
`);
`,
);
const code = functionName + '(' + red + ', ' + green + ', ' + blue + ')';
return [code, Order.FUNCTION_CALL];
};
}

export function colour_blend(block: Block, generator: JavascriptGenerator): [string, Order] {
export function colour_blend(
block: Block,
generator: JavascriptGenerator,
): [string, Order] {
// Blend two colours together.
const c1 = generator.valueToCode(block, 'COLOUR1', Order.NONE) ||
"'#000000'";
const c2 = generator.valueToCode(block, 'COLOUR2', Order.NONE) ||
"'#000000'";
const ratio =
generator.valueToCode(block, 'RATIO', Order.NONE) || 0.5;
const functionName = generator.provideFunction_('colourBlend', `
const c1 = generator.valueToCode(block, 'COLOUR1', Order.NONE) || "'#000000'";
const c2 = generator.valueToCode(block, 'COLOUR2', Order.NONE) || "'#000000'";
const ratio = generator.valueToCode(block, 'RATIO', Order.NONE) || 0.5;
const functionName = generator.provideFunction_(
'colourBlend',
`
function ${generator.FUNCTION_NAME_PLACEHOLDER_}(c1, c2, ratio) {
ratio = Math.max(Math.min(Number(ratio), 1), 0);
var r1 = parseInt(c1.substring(1, 3), 16);
Expand All @@ -80,7 +94,8 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(c1, c2, ratio) {
b = ('0' + (b || 0).toString(16)).slice(-2);
return '#' + r + g + b;
}
`);
`,
);
const code = functionName + '(' + c1 + ', ' + c2 + ', ' + ratio + ')';
return [code, Order.FUNCTION_CALL];
};
}
39 changes: 24 additions & 15 deletions generators/javascript/javascript_generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ import {Names, NameType} from '../../core/names.js';
import type {Workspace} from '../../core/workspace.js';
import {inputTypes} from '../../core/inputs/input_types.js';


/**
* Order of operation ENUMs.
* https://developer.mozilla.org/en/JavaScript/Reference/Operators/Operator_Precedence
*/
// prettier-ignore
export enum Order {
ATOMIC = 0, // 0 "" ...
NEW = 1.1, // new
Expand Down Expand Up @@ -60,7 +60,7 @@ export enum Order {
YIELD = 17, // yield
COMMA = 18, // ,
NONE = 99, // (...)
};
}

/**
* JavaScript code generator class.
Expand Down Expand Up @@ -93,7 +93,7 @@ export class JavascriptGenerator extends CodeGenerator {
// a && (b && c) -> a && b && c
[Order.LOGICAL_AND, Order.LOGICAL_AND],
// a || (b || c) -> a || b || c
[Order.LOGICAL_OR, Order.LOGICAL_OR]
[Order.LOGICAL_OR, Order.LOGICAL_OR],
];

/** @param name Name of language generator is for. */
cpcallen marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -126,8 +126,8 @@ export class JavascriptGenerator extends CodeGenerator {
// this list is trivial. This is intended to prevent users from
// accidentally clobbering a built-in object or function.
this.addReservedWords(
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords
'break,case,catch,class,const,continue,debugger,default,delete,do,' +
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords
'break,case,catch,class,const,continue,debugger,default,delete,do,' +
'else,export,extends,finally,for,function,if,import,in,instanceof,' +
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a weird thing it did. Might want to report this as a prettier bug. Or just move the comment to outside the parameter list (above this.addReservedWords instead).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I moved the comment since I thought that worth doing anyway, but it did not result in prettier formatting the argument any differently, and indeed I think it is actually doing the right thing here: there is a single argument to addReservedWords, and that argument is in fact split over multiple lines, and the normal rule is that continuation lines should be indented (+4 in Google style, +2 in Prettier style).

'new,return,super,switch,this,throw,try,typeof,var,void,' +
'while,with,yield,' +
Expand All @@ -139,7 +139,7 @@ export class JavascriptGenerator extends CodeGenerator {
'arguments,' +
// Everything in the current environment (835 items in Chrome,
// 104 in Node).
Object.getOwnPropertyNames(globalThis).join(',')
Object.getOwnPropertyNames(globalThis).join(','),
);
}

Expand All @@ -166,14 +166,16 @@ export class JavascriptGenerator extends CodeGenerator {
const devVarList = Variables.allDeveloperVariables(workspace);
for (let i = 0; i < devVarList.length; i++) {
defvars.push(
this.nameDB_.getName(devVarList[i], NameType.DEVELOPER_VARIABLE));
this.nameDB_.getName(devVarList[i], NameType.DEVELOPER_VARIABLE),
);
}

// Add user variables, but only ones that are being used.
const variables = Variables.allUsedVarModels(workspace);
for (let i = 0; i < variables.length; i++) {
defvars.push(
this.nameDB_.getName(variables[i].getId(), NameType.VARIABLE));
this.nameDB_.getName(variables[i].getId(), NameType.VARIABLE),
);
}

// Declare all of the variables.
Expand Down Expand Up @@ -221,10 +223,11 @@ export class JavascriptGenerator extends CodeGenerator {
quote_(string: string): string {
// Can't use goog.string.quote since Google's style guide recommends
// JS string literals use single quotes.
string = string.replace(/\\/g, '\\\\')
.replace(/\n/g, '\\\n')
.replace(/'/g, '\\\'');
return '\'' + string + '\'';
string = string
.replace(/\\/g, '\\\\')
.replace(/\n/g, '\\\n')
.replace(/'/g, "\\'");
return "'" + string + "'";
}

/**
Expand All @@ -237,7 +240,7 @@ export class JavascriptGenerator extends CodeGenerator {
// Can't use goog.string.quote since Google's style guide recommends
// JS string literals use single quotes.
const lines = string.split(/\n/g).map(this.quote_);
return lines.join(' + \'\\n\' +\n');
return lines.join(" + '\\n' +\n");
}

/**
Expand Down Expand Up @@ -276,7 +279,7 @@ export class JavascriptGenerator extends CodeGenerator {
}
}
const nextBlock =
block.nextConnection && block.nextConnection.targetBlock();
block.nextConnection && block.nextConnection.targetBlock();
const nextCode = thisOnly ? '' : this.blockToCode(nextBlock);
return commentCode + code + nextCode;
}
Expand All @@ -293,7 +296,13 @@ export class JavascriptGenerator extends CodeGenerator {
* @param order The highest order acting on this value.
* @returns The adjusted value.
*/
getAdjusted(block: Block, atId: string, delta = 0, negate = false, order = Order.NONE): string|number {
getAdjusted(
block: Block,
atId: string,
delta = 0,
negate = false,
order = Order.NONE,
): string | number {
if (block.workspace.options.oneBasedIndex) {
delta--;
}
Expand Down
Loading