@turbowarp/sb3fix 0.3.2 → 0.3.4

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.
Files changed (2) hide show
  1. package/package.json +3 -3
  2. package/src/sb3fix.js +63 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turbowarp/sb3fix",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Fix corrupted Scratch projects",
5
5
  "main": "src/sb3fix.js",
6
6
  "scripts": {
@@ -21,11 +21,11 @@
21
21
  },
22
22
  "homepage": "https://github.com/TurboWarp/sb3fix#readme",
23
23
  "dependencies": {
24
- "@turbowarp/jszip": "^3.11.0"
24
+ "@turbowarp/jszip": "^3.11.1"
25
25
  },
26
26
  "devDependencies": {
27
27
  "copy-webpack-plugin": "^12.0.2",
28
- "webpack": "^5.90.3",
28
+ "webpack": "^5.96.1",
29
29
  "webpack-cli": "^5.1.4"
30
30
  }
31
31
  }
package/src/sb3fix.js CHANGED
@@ -199,6 +199,29 @@ const fixJSON = (data, options = {}) => {
199
199
  }
200
200
  };
201
201
 
202
+ /**
203
+ * @param {string} id
204
+ * @param {unknown} comment
205
+ */
206
+ const fixCommentInPlace = (id, comment) => {
207
+ if (!isObject(comment)) {
208
+ throw new Error('comment is not an object');
209
+ }
210
+
211
+ if (typeof comment.text !== 'string') {
212
+ throw new Error('comment text is not a string');
213
+ }
214
+
215
+ // Scratch requires comments to not exceed 8000 characters.
216
+ // We'll store the excess in .extraText so the text won't be truncated if opened in TurboWarp.
217
+ const MAX_LENGTH = 8000;
218
+ if (comment.text.length > MAX_LENGTH) {
219
+ log(`comment ${id} had length ${comment.text.length}`);
220
+ comment.extraText = comment.text.substring(MAX_LENGTH);
221
+ comment.text = comment.text.substring(0, MAX_LENGTH);
222
+ }
223
+ };
224
+
202
225
  /**
203
226
  * @param {unknown} target
204
227
  */
@@ -287,6 +310,14 @@ const fixJSON = (data, options = {}) => {
287
310
  fixBlockInPlace(blockId, block);
288
311
  }
289
312
 
313
+ // Comments are not required
314
+ const comments = target.comments;
315
+ if (comments) {
316
+ for (const [commentId, comment] of Object.entries(comments)) {
317
+ fixCommentInPlace(commentId, comment);
318
+ }
319
+ }
320
+
290
321
  const variables = target.variables;
291
322
  if (!isObject(variables)) {
292
323
  throw new Error('variables is not an object');
@@ -314,6 +345,16 @@ const fixJSON = (data, options = {}) => {
314
345
  target.layerOrder = 1;
315
346
  }
316
347
  }
348
+
349
+ const ROTATION_STYLES = [
350
+ 'all around',
351
+ 'don\'t rotate',
352
+ 'left-right'
353
+ ];
354
+ if (!target.isStage && !ROTATION_STYLES.includes(target.rotationStyle)) {
355
+ log(`sprite had invalid rotation style ${target.rotationStyle}`);
356
+ target.rotationStyle = 'all around';
357
+ }
317
358
  };
318
359
 
319
360
  /**
@@ -351,11 +392,10 @@ const fixJSON = (data, options = {}) => {
351
392
  fixTargetInPlace(target);
352
393
  }
353
394
 
354
- let stage;
355
395
  const allStages = targets.filter((target) => target.isStage);
356
396
  if (allStages.length === 0) {
357
397
  log('stage is missing; adding an empty one');
358
- stage = {
398
+ targets.unshift({
359
399
  isStage: true,
360
400
  name: 'Stage',
361
401
  variables: {},
@@ -380,22 +420,31 @@ const fixJSON = (data, options = {}) => {
380
420
  videoTransparency: 50,
381
421
  videoState: "on",
382
422
  textToSpeechLanguage: null
383
- };
384
- targets.unshift(stage);
385
- } else if (allStages.length === 1) {
386
- const stageIndex = targets.findIndex((target) => target.isStage);
387
- // stageIndex guaranteed to not be -1 by earlier filter check
388
- stage = targets[stageIndex];
389
- // stage must be the first target
390
- if (stageIndex !== 0) {
391
- log(`stage was at wrong index: ${stageIndex}`);
392
- targets.splice(stageIndex, 1);
423
+ });
424
+ } else {
425
+ // We will accept the first stage in targets as the real stage
426
+ const firstStageIndex = targets.findIndex((target) => target.isStage);
427
+
428
+ // Stage must be the first target
429
+ if (firstStageIndex !== 0) {
430
+ log(`stage was at wrong index: ${firstStageIndex}`);
431
+ const stage = targets[firstStageIndex];
432
+ targets.splice(firstStageIndex, 1);
393
433
  targets.unshift(stage);
394
434
  }
395
- } else {
396
- throw new Error(`wrong number of stages: ${allStages.length}`);
435
+
436
+ // Remove all the other stages
437
+ for (let i = targets.length - 1; i > 0; i--) {
438
+ if (targets[i].isStage) {
439
+ log(`removing extra stage at index ${i}`);
440
+ targets.splice(i, 1);
441
+ }
442
+ }
397
443
  }
398
444
 
445
+ // Above checks ensure this invariant holds
446
+ const stage = targets[0];
447
+
399
448
  // stage's name must match exactly
400
449
  if (stage.name !== 'Stage') {
401
450
  log(`stage had wrong name: ${stage.name}`);