@triflare/fx 2.1.0-beta.5 → 2.2.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@triflare/fx",
3
- "version": "2.1.0-beta.5",
3
+ "version": "2.2.0-alpha.1",
4
4
  "description": "Code PenguinMod projects with text.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -18,6 +18,10 @@
18
18
  "./language": "./src/language.js",
19
19
  "./lint": "./src/lint/analyze.js"
20
20
  },
21
+ "publishConfig": {
22
+ "access": "public",
23
+ "provenance": true
24
+ },
21
25
  "files": [
22
26
  "bin",
23
27
  "src",
@@ -51,8 +55,8 @@
51
55
  "commander": "^15.0.0",
52
56
  "cross-fetch": "^4.1.0",
53
57
  "decode-html": "2.0.0",
54
- "format-message": "6.2.1",
55
- "htmlparser2": "^3.10.0",
58
+ "format-message": "6.2.4",
59
+ "htmlparser2": "^12.0.0",
56
60
  "immutable": "^3.8.4",
57
61
  "js-yaml": "^5.4.3",
58
62
  "jszip": "^3.10.1",
@@ -64,10 +68,15 @@
64
68
  },
65
69
  "devDependencies": {
66
70
  "@eslint/js": "^10.0.1",
71
+ "@semantic-release/changelog": "^7.0.0",
72
+ "@semantic-release/exec": "^7.1.0",
73
+ "@semantic-release/git": "^11.0.1",
74
+ "conventional-changelog-conventionalcommits": "^8.0.0",
67
75
  "eslint": "^10.14.0",
68
76
  "eslint-config-prettier": "^10.1.8",
69
77
  "globals": "^17.13.0",
70
- "prettier": "^3.9.10"
78
+ "prettier": "^3.9.10",
79
+ "semantic-release": "^25.0.9"
71
80
  },
72
81
  "overrides": {
73
82
  "minimatch": "^11.0.4",
package/product.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  # Identity
2
2
  name: "Triflare Fx"
3
3
  command: "fx"
4
- version: "2.1.0-beta.5"
4
+ version: "2.2.0-alpha.1"
5
5
  tagline: "Code PenguinMod projects with text."
6
6
  author: "Triflare"
7
7
  homepage: "https://github.com/triflare/fx"
package/src/catalog.js CHANGED
@@ -13,6 +13,9 @@
13
13
  * - `args` describes a hat's parameters (`field` or `input`).
14
14
  * - `extension` names the providing extension for non-core opcodes.
15
15
  * - `dynamicNext` / `stopBlock` flag blocks with special stack behaviour.
16
+ * - `extendable` marks a two-input block whose call may pass extra values;
17
+ * compilation folds them into a chain of the same block (see the
18
+ * compiler's `emitExtendableCall`).
16
19
  *
17
20
  * Input descriptors:
18
21
  * NUMBER, STRING, BOOLEAN, ANGLE, COLOR, INTEGER, POSITIVE, WHOLE, SUBSTACK
@@ -387,21 +390,25 @@ const CORE_CATALOG = {
387
390
  "operators.add": {
388
391
  opcode: "operator_add",
389
392
  kind: "reporter",
393
+ extendable: true,
390
394
  inputs: { NUM1: NUMBER, NUM2: NUMBER },
391
395
  },
392
396
  "operators.subtract": {
393
397
  opcode: "operator_subtract",
394
398
  kind: "reporter",
399
+ extendable: true,
395
400
  inputs: { NUM1: NUMBER, NUM2: NUMBER },
396
401
  },
397
402
  "operators.multiply": {
398
403
  opcode: "operator_multiply",
399
404
  kind: "reporter",
405
+ extendable: true,
400
406
  inputs: { NUM1: NUMBER, NUM2: NUMBER },
401
407
  },
402
408
  "operators.divide": {
403
409
  opcode: "operator_divide",
404
410
  kind: "reporter",
411
+ extendable: true,
405
412
  inputs: { NUM1: NUMBER, NUM2: NUMBER },
406
413
  },
407
414
  "operators.random": {
@@ -427,11 +434,13 @@ const CORE_CATALOG = {
427
434
  "operators.and": {
428
435
  opcode: "operator_and",
429
436
  kind: "boolean",
437
+ extendable: true,
430
438
  inputs: { OPERAND1: BOOLEAN, OPERAND2: BOOLEAN },
431
439
  },
432
440
  "operators.or": {
433
441
  opcode: "operator_or",
434
442
  kind: "boolean",
443
+ extendable: true,
435
444
  inputs: { OPERAND1: BOOLEAN, OPERAND2: BOOLEAN },
436
445
  },
437
446
  "operators.not": {
@@ -442,6 +451,7 @@ const CORE_CATALOG = {
442
451
  "operators.join": {
443
452
  opcode: "operator_join",
444
453
  kind: "reporter",
454
+ extendable: true,
445
455
  inputs: { STRING1: STRING, STRING2: STRING },
446
456
  },
447
457
  "operators.letterOf": {
@@ -462,6 +472,7 @@ const CORE_CATALOG = {
462
472
  "operators.mod": {
463
473
  opcode: "operator_mod",
464
474
  kind: "reporter",
475
+ extendable: true,
465
476
  inputs: { NUM1: NUMBER, NUM2: NUMBER },
466
477
  },
467
478
  "operators.round": {
package/src/compiler.js CHANGED
@@ -1076,10 +1076,22 @@ export class Compiler {
1076
1076
  if (id && !this.requiredExtensions.has(id))
1077
1077
  this.requiredExtensions.set(id, node.loc);
1078
1078
  }
1079
- const id = this.addBlock(ctx, { opcode: entry.opcode, parent: parentId });
1080
1079
  const fieldNames = Object.keys(entry.fields || {});
1081
1080
  const inputNames = Object.keys(entry.inputs || {});
1082
1081
 
1082
+ // Extendable blocks take any number of arguments. A call with more than
1083
+ // the block's own inputs folds into a left-associated chain of the same
1084
+ // two-input vanilla block, so `operators.join(a, b, c)` becomes
1085
+ // `join(join(a, b), c)` and still loads in plain Scratch.
1086
+ if (
1087
+ entry.extendable &&
1088
+ node.args.length > inputNames.length + fieldNames.length
1089
+ ) {
1090
+ return this.emitExtendableCall(ctx, node, entry, parentId);
1091
+ }
1092
+
1093
+ const id = this.addBlock(ctx, { opcode: entry.opcode, parent: parentId });
1094
+
1083
1095
  if (node.args.length > inputNames.length + fieldNames.length) {
1084
1096
  this.fail(
1085
1097
  `${node.name} expects at most ${inputNames.length + fieldNames.length} argument(s)`,
@@ -1160,6 +1172,56 @@ export class Compiler {
1160
1172
  return { id, kind: entry.kind, entry };
1161
1173
  }
1162
1174
 
1175
+ /**
1176
+ * Emit an extendable block called with more arguments than it has inputs.
1177
+ * The first two arguments fill the block and every further argument wraps
1178
+ * the result so far in another copy, keeping left-associative order:
1179
+ * `operators.subtract(10, 3, 2)` is `(10 - 3) - 2`.
1180
+ * @param {Context} ctx
1181
+ * @param {object} node
1182
+ * @param {object} entry
1183
+ * @param {string|null} parentId
1184
+ */
1185
+ emitExtendableCall(ctx, node, entry, parentId) {
1186
+ const specs = entry.inputs || {};
1187
+ const inputNames = Object.keys(specs);
1188
+ if (Object.keys(entry.fields || {}).length || inputNames.length !== 2)
1189
+ this.fail(
1190
+ `Internal error: ${node.name} is marked extendable but is not a two-input block`,
1191
+ node.loc,
1192
+ );
1193
+
1194
+ const [first, second] = inputNames;
1195
+ // A chain position holds a reporter directly; boolean inputs take the
1196
+ // reporter without the shadow-value fallback.
1197
+ const chainInput = (spec, id) =>
1198
+ spec.type === "boolean" ? [2, id] : [3, id, this.defaultPrim(spec)];
1199
+
1200
+ let id = this.addBlock(ctx, { opcode: entry.opcode, parent: parentId });
1201
+ let inputs = {
1202
+ [first]: this.buildInputForSpec(specs[first], node.args[0], id),
1203
+ [second]: this.buildInputForSpec(specs[second], node.args[1], id),
1204
+ };
1205
+ ctx.target.blocks[id].inputs = inputs;
1206
+ this.reparentInputs(ctx, id, inputs);
1207
+
1208
+ for (let i = 2; i < node.args.length; i++) {
1209
+ const wrapId = this.addBlock(ctx, {
1210
+ opcode: entry.opcode,
1211
+ parent: parentId,
1212
+ });
1213
+ inputs = {
1214
+ [first]: chainInput(specs[first], id),
1215
+ [second]: this.buildInputForSpec(specs[second], node.args[i], wrapId),
1216
+ };
1217
+ ctx.target.blocks[wrapId].inputs = inputs;
1218
+ this.reparentInputs(ctx, wrapId, inputs);
1219
+ id = wrapId;
1220
+ }
1221
+
1222
+ return { id, kind: entry.kind, entry };
1223
+ }
1224
+
1163
1225
  emitListCall(ctx, node, listInfo, parentId) {
1164
1226
  const method = node.segments[1];
1165
1227
  let spec = null;
@@ -2,5 +2,5 @@
2
2
  "name": "@penguinmod/packager",
3
3
  "version": "1.9.2",
4
4
  "commit": "53f868b85ec21d2edab9ce00fd2d709da93da6d6",
5
- "builtAt": "2026-11-06T05:46:49.113Z"
5
+ "builtAt": "2026-11-13T05:41:23.450Z"
6
6
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "scratch-vm",
3
3
  "version": "0.2.0",
4
- "commit": "75ef6994e55270b9c2a2f3cb2edf6a5bf31cdf2a",
4
+ "commit": "50fddfed34f4eed9eef28ef32bc1022397d29f46",
5
5
  "inputHash": "9557b90be391ad2f1deaccfd034fddf0a9591c354da50b13e4cf142a8e2654fc",
6
- "builtAt": "2026-11-07T18:15:07.603Z"
6
+ "builtAt": "2026-11-13T05:43:32.997Z"
7
7
  }
@@ -2,5 +2,5 @@ This directory contains the PenguinMod VM.
2
2
 
3
3
  Upstream: https://github.com/PenguinMod/PenguinMod-Vm
4
4
  Version: 0.2.0
5
- Commit: 75ef6994e55270b9c2a2f3cb2edf6a5bf31cdf2a
5
+ Commit: 50fddfed34f4eed9eef28ef32bc1022397d29f46
6
6
  License: BSD-3-Clause (see LICENSE)