pxt-core 9.2.9 → 9.2.11

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/built/pxt.js CHANGED
@@ -105304,251 +105304,11 @@ var pxt;
105304
105304
  (function (crowdin) {
105305
105305
  crowdin.KEY_VARIABLE = "CROWDIN_KEY";
105306
105306
  crowdin.testMode = false;
105307
- crowdin.TEST_KEY = "!!!testmode!!!";
105308
105307
  function setTestMode() {
105309
105308
  pxt.crowdin.testMode = true;
105310
105309
  pxt.log(`CROWDIN TEST MODE - files will NOT be uploaded`);
105311
105310
  }
105312
105311
  crowdin.setTestMode = setTestMode;
105313
- function multipartPostAsync(key, uri, data = {}, filename = null, filecontents = null) {
105314
- if (crowdin.testMode || key == crowdin.TEST_KEY) {
105315
- const resp = {
105316
- success: true
105317
- };
105318
- return Promise.resolve({ statusCode: 200, headers: {}, text: JSON.stringify(resp), json: resp });
105319
- }
105320
- return pxt.Util.multipartPostAsync(uri, data, filename, filecontents);
105321
- }
105322
- function apiUri(branch, prj, key, cmd, args) {
105323
- pxt.Util.assert(!!prj && !!key && !!cmd);
105324
- const apiRoot = "https://api.crowdin.com/api/project/" + prj + "/";
105325
- args = args || {};
105326
- if (crowdin.testMode)
105327
- delete args["key"]; // ensure no key is passed in test mode
105328
- else
105329
- args["key"] = key;
105330
- if (branch)
105331
- args["branch"] = branch;
105332
- return apiRoot + cmd + "?" + Object.keys(args).map(k => `${k}=${encodeURIComponent(args[k])}`).join("&");
105333
- }
105334
- function downloadTranslationsAsync(branch, prj, key, filename, options = {}) {
105335
- const q = { json: "true" };
105336
- const infoUri = apiUri(branch, prj, key, "info", q);
105337
- const r = {};
105338
- filename = normalizeFileName(filename);
105339
- return pxt.Util.httpGetTextAsync(infoUri).then(respText => {
105340
- const info = JSON.parse(respText);
105341
- if (!info)
105342
- throw new Error("info failed");
105343
- let todo = info.languages.filter(l => l.code != "en");
105344
- if (pxt.appTarget && pxt.appTarget.appTheme && pxt.appTarget.appTheme.availableLocales)
105345
- todo = todo.filter(l => pxt.appTarget.appTheme.availableLocales.indexOf(l.code) > -1);
105346
- pxt.log('languages: ' + todo.map(l => l.code).join(', '));
105347
- const nextFile = () => {
105348
- const item = todo.pop();
105349
- if (!item)
105350
- return Promise.resolve();
105351
- const exportFileUri = apiUri(branch, prj, key, "export-file", {
105352
- file: filename,
105353
- language: item.code,
105354
- export_translated_only: options.translatedOnly ? "1" : "0",
105355
- export_approved_only: options.validatedOnly ? "1" : "0"
105356
- });
105357
- pxt.log(`downloading ${item.name} - ${item.code} (${todo.length} more)`);
105358
- return pxt.Util.httpGetTextAsync(exportFileUri).then((transationsText) => {
105359
- try {
105360
- const translations = JSON.parse(transationsText);
105361
- if (translations)
105362
- r[item.code] = translations;
105363
- }
105364
- catch (e) {
105365
- pxt.log(exportFileUri + ' ' + e);
105366
- }
105367
- return nextFile();
105368
- }).then(() => pxt.Util.delay(1000)); // throttling otherwise crowdin fails
105369
- };
105370
- return nextFile();
105371
- }).then(() => r);
105372
- }
105373
- crowdin.downloadTranslationsAsync = downloadTranslationsAsync;
105374
- function mkIncr(filename) {
105375
- let cnt = 0;
105376
- return function incr() {
105377
- if (cnt++ > 10) {
105378
- throw new Error("Too many API calls for " + filename);
105379
- }
105380
- };
105381
- }
105382
- function createDirectoryAsync(branch, prj, key, name, incr) {
105383
- name = normalizeFileName(name);
105384
- pxt.debug(`create directory ${branch || ""}/${name}`);
105385
- if (!incr)
105386
- incr = mkIncr(name);
105387
- return multipartPostAsync(key, apiUri(branch, prj, key, "add-directory"), { json: "true", name: name })
105388
- .then(resp => {
105389
- pxt.debug(`crowdin resp: ${resp.statusCode}`);
105390
- // 400 returned by folder already exists
105391
- if (resp.statusCode == 200 || resp.statusCode == 400)
105392
- return Promise.resolve();
105393
- if (resp.statusCode == 500 && resp.text) {
105394
- const json = JSON.parse(resp.text);
105395
- if (json.error.code === 50) {
105396
- pxt.log('directory already exists');
105397
- return Promise.resolve();
105398
- }
105399
- }
105400
- const data = resp.json || JSON.parse(resp.text) || { error: {} };
105401
- if (resp.statusCode == 404 && data.error.code == 17) {
105402
- pxt.log(`parent directory missing for ${name}`);
105403
- const par = name.replace(/\/[^\/]+$/, "");
105404
- if (par != name) {
105405
- return createDirectoryAsync(branch, prj, key, par, incr)
105406
- .then(() => createDirectoryAsync(branch, prj, key, name, incr)); // retry
105407
- }
105408
- }
105409
- throw new Error(`cannot create directory ${branch || ""}/${name}: ${resp.statusCode} ${JSON.stringify(data)}`);
105410
- });
105411
- }
105412
- crowdin.createDirectoryAsync = createDirectoryAsync;
105413
- function normalizeFileName(filename) {
105414
- return filename.replace(/\\/g, '/');
105415
- }
105416
- function uploadTranslationAsync(branch, prj, key, filename, data) {
105417
- pxt.Util.assert(!!prj);
105418
- pxt.Util.assert(!!key);
105419
- filename = normalizeFileName(filename);
105420
- const incr = mkIncr(filename);
105421
- function startAsync() {
105422
- return uploadAsync("update-file", { update_option: "update_as_unapproved" });
105423
- }
105424
- function uploadAsync(op, opts) {
105425
- opts["type"] = "auto";
105426
- opts["json"] = "";
105427
- opts["escape_quotes"] = "0";
105428
- incr();
105429
- return multipartPostAsync(key, apiUri(branch, prj, key, op), opts, filename, data)
105430
- .then(resp => handleResponseAsync(resp));
105431
- }
105432
- function handleResponseAsync(resp) {
105433
- var _a, _b, _c;
105434
- const code = resp.statusCode;
105435
- const errorData = pxt.Util.jsonTryParse(resp.text) || {};
105436
- pxt.debug(`upload result: ${code}`);
105437
- if (code == 404 && errorData.error && errorData.error.code == 8) {
105438
- pxt.log(`create new translation file: ${filename}`);
105439
- return uploadAsync("add-file", {});
105440
- }
105441
- else if (code == 404 && ((_a = errorData.error) === null || _a === void 0 ? void 0 : _a.code) == 17) {
105442
- return createDirectoryAsync(branch, prj, key, filename.replace(/\/[^\/]+$/, ""), incr)
105443
- .then(() => startAsync());
105444
- }
105445
- else if (!errorData.success && ((_b = errorData.error) === null || _b === void 0 ? void 0 : _b.code) == 53) {
105446
- // file is being updated
105447
- pxt.log(`${filename} being updated, waiting 5s and retry...`);
105448
- return pxt.U.delay(5000) // wait 5s and try again
105449
- .then(() => uploadTranslationAsync(branch, prj, key, filename, data));
105450
- }
105451
- else if (code == 429 && ((_c = errorData.error) === null || _c === void 0 ? void 0 : _c.code) == 55) {
105452
- // Too many concurrent requests
105453
- pxt.log(`Maximum concurrent requests reached, waiting 10s and retry...`);
105454
- return pxt.U.delay(10 * 1000) // wait 10s and try again
105455
- .then(() => uploadTranslationAsync(branch, prj, key, filename, data));
105456
- }
105457
- else if (code == 200 || errorData.success) {
105458
- // something crowdin reports 500 with success=true
105459
- return Promise.resolve();
105460
- }
105461
- else {
105462
- throw new Error(`Error, upload translation: ${filename}, ${code}, ${resp.text}`);
105463
- }
105464
- }
105465
- return startAsync();
105466
- }
105467
- crowdin.uploadTranslationAsync = uploadTranslationAsync;
105468
- function flatten(allFiles, node, parentDir, branch) {
105469
- const n = node.name;
105470
- const d = parentDir ? parentDir + "/" + n : n;
105471
- node.fullName = d;
105472
- node.branch = branch || "";
105473
- switch (node.node_type) {
105474
- case "file":
105475
- allFiles.push(node);
105476
- break;
105477
- case "directory":
105478
- (node.files || []).forEach(f => flatten(allFiles, f, d, branch));
105479
- break;
105480
- case "branch":
105481
- (node.files || []).forEach(f => flatten(allFiles, f, parentDir, node.name));
105482
- break;
105483
- }
105484
- }
105485
- function filterAndFlattenFiles(files, crowdinPath) {
105486
- const pxtCrowdinBranch = pxt.appTarget.versions.pxtCrowdinBranch || "";
105487
- const targetCrowdinBranch = pxt.appTarget.versions.targetCrowdinBranch || "";
105488
- let allFiles = [];
105489
- // flatten the files
105490
- files.forEach(f => flatten(allFiles, f, ""));
105491
- // top level files are for PXT, subolder are targets
105492
- allFiles = allFiles.filter(f => {
105493
- if (f.fullName.indexOf('/') < 0)
105494
- return f.branch == pxtCrowdinBranch; // pxt file
105495
- else
105496
- return f.branch == targetCrowdinBranch;
105497
- });
105498
- // folder filter
105499
- if (crowdinPath) {
105500
- // filter out crowdin folder
105501
- allFiles = allFiles.filter(f => f.fullName.indexOf(crowdinPath) == 0);
105502
- }
105503
- // filter out non-target files
105504
- if (pxt.appTarget.id != "core") {
105505
- const id = pxt.appTarget.id + '/';
105506
- allFiles = allFiles.filter(f => {
105507
- return f.fullName.indexOf('/') < 0 // top level file
105508
- || f.fullName.substr(0, id.length) == id // from the target folder
105509
- || f.fullName.indexOf('common-docs') >= 0; // common docs
105510
- });
105511
- }
105512
- return allFiles;
105513
- }
105514
- function projectInfoAsync(prj, key) {
105515
- const q = { json: "true" };
105516
- const infoUri = apiUri("", prj, key, "info", q);
105517
- return pxt.Util.httpGetTextAsync(infoUri).then(respText => {
105518
- const info = JSON.parse(respText);
105519
- return info;
105520
- });
105521
- }
105522
- crowdin.projectInfoAsync = projectInfoAsync;
105523
- /**
105524
- * Scans files in crowdin and report files that are not on disk anymore
105525
- */
105526
- function listFilesAsync(prj, key, crowdinPath) {
105527
- pxt.log(`crowdin: listing files under ${crowdinPath}`);
105528
- return projectInfoAsync(prj, key)
105529
- .then(info => {
105530
- if (!info)
105531
- throw new Error("info failed");
105532
- let allFiles = filterAndFlattenFiles(info.files, crowdinPath);
105533
- pxt.debug(`crowdin: found ${allFiles.length} under ${crowdinPath}`);
105534
- return allFiles.map(f => {
105535
- return {
105536
- fullName: f.fullName,
105537
- branch: f.branch || ""
105538
- };
105539
- });
105540
- });
105541
- }
105542
- crowdin.listFilesAsync = listFilesAsync;
105543
- function languageStatsAsync(prj, key, lang) {
105544
- const uri = apiUri("", prj, key, "language-status", { language: lang, json: "true" });
105545
- return pxt.Util.httpGetJsonAsync(uri)
105546
- .then(info => {
105547
- const allFiles = filterAndFlattenFiles(info.files);
105548
- return allFiles;
105549
- });
105550
- }
105551
- crowdin.languageStatsAsync = languageStatsAsync;
105552
105312
  function inContextLoadAsync(text) {
105553
105313
  const node = document.createElement("input");
105554
105314
  node.type = "text";
@@ -111039,11 +110799,12 @@ var pxt;
111039
110799
  return result;
111040
110800
  }
111041
110801
  sprite_1.resizeTilemap = resizeTilemap;
111042
- function imageLiteralToBitmap(text) {
110802
+ function imageLiteralToBitmap(text, templateLiteral = "img") {
111043
110803
  // Strip the tagged template string business and the whitespace. We don't have to exhaustively
111044
110804
  // replace encoded characters because the compiler will catch any disallowed characters and throw
111045
110805
  // an error before the decompilation happens. 96 is backtick and 9 is tab
111046
110806
  text = text.replace(/[ `]|(?:&#96;)|(?:&#9;)|(?:img)/g, "").trim();
110807
+ text = text.replaceAll(templateLiteral, "");
111047
110808
  text = text.replace(/^["`\(\)]*/, '').replace(/["`\(\)]*$/, '');
111048
110809
  text = text.replace(/&#10;/g, "\n");
111049
110810
  const rows = text.split("\n");
@@ -111205,14 +110966,14 @@ var pxt;
111205
110966
  pxt.sprite.trimTilemapTileset(result);
111206
110967
  }
111207
110968
  sprite_1.updateTilemapReferencesFromResult = updateTilemapReferencesFromResult;
111208
- function imageLiteralPrologue(fileType) {
110969
+ function imageLiteralPrologue(fileType, templateLiteral = "img") {
111209
110970
  let res = '';
111210
110971
  switch (fileType) {
111211
110972
  case "python":
111212
- res = "img(\"\"\"";
110973
+ res = `${templateLiteral}("""`;
111213
110974
  break;
111214
110975
  default:
111215
- res = "img`";
110976
+ res = `${templateLiteral}\``;
111216
110977
  break;
111217
110978
  }
111218
110979
  return res;
@@ -111243,10 +111004,10 @@ var pxt;
111243
111004
  return res;
111244
111005
  }
111245
111006
  sprite_1.imageLiteralFromDimensions = imageLiteralFromDimensions;
111246
- function bitmapToImageLiteral(bitmap, fileType) {
111007
+ function bitmapToImageLiteral(bitmap, fileType, templateLiteral = "img") {
111247
111008
  if (!bitmap || bitmap.height === 0 || bitmap.width === 0)
111248
111009
  return "";
111249
- let res = imageLiteralPrologue(fileType);
111010
+ let res = imageLiteralPrologue(fileType, templateLiteral);
111250
111011
  if (bitmap) {
111251
111012
  const paddingBetweenPixels = (bitmap.width * bitmap.height > 300) ? "" : " ";
111252
111013
  for (let r = 0; r < bitmap.height; r++) {
@@ -113747,11 +113508,7 @@ var pxt;
113747
113508
  };
113748
113509
  await loadDepsRecursive(null, this);
113749
113510
  // get paletter config loading deps, so the more higher level packages take precedence
113750
- if (this.config.palette && pxt.appTarget.runtime) {
113751
- pxt.appTarget.runtime.palette = pxt.U.clone(this.config.palette);
113752
- if (this.config.paletteNames)
113753
- pxt.appTarget.runtime.paletteNames = this.config.paletteNames;
113754
- }
113511
+ this.patchAppTargetPalette();
113755
113512
  // get screen size loading deps, so the more higher level packages take precedence
113756
113513
  if (this.config.screenSize && pxt.appTarget.runtime)
113757
113514
  pxt.appTarget.runtime.screenSize = pxt.U.clone(this.config.screenSize);
@@ -113897,6 +113654,13 @@ var pxt;
113897
113654
  }
113898
113655
  return r;
113899
113656
  }
113657
+ patchAppTargetPalette() {
113658
+ if (this.config.palette && pxt.appTarget.runtime) {
113659
+ pxt.appTarget.runtime.palette = pxt.U.clone(this.config.palette);
113660
+ if (this.config.paletteNames)
113661
+ pxt.appTarget.runtime.paletteNames = this.config.paletteNames;
113662
+ }
113663
+ }
113900
113664
  }
113901
113665
  Package.depWarnings = {};
113902
113666
  pxt.Package = Package;
@@ -114158,6 +113922,10 @@ var pxt;
114158
113922
  opts.jres = this.getJRes();
114159
113923
  const functionOpts = pxt.appTarget.runtime && pxt.appTarget.runtime.functionsOptions;
114160
113924
  opts.allowedArgumentTypes = functionOpts && functionOpts.extraFunctionEditorTypes && functionOpts.extraFunctionEditorTypes.map(info => info.typeName).concat("number", "boolean", "string");
113925
+ for (const dep of this.sortedDeps()) {
113926
+ dep.patchAppTargetPalette();
113927
+ }
113928
+ this.patchAppTargetPalette();
114161
113929
  return opts;
114162
113930
  }
114163
113931
  prepareConfigToBePublished() {
@@ -118932,7 +118700,7 @@ var pxt;
118932
118700
  if (!steps)
118933
118701
  return undefined; // error parsing steps
118934
118702
  // collect code and infer editor
118935
- const { code, templateCode, editor, language, jres, assetJson, customTs, simThemeJson } = computeBodyMetadata(body);
118703
+ const { code, templateCode, templateLanguage, editor, language, jres, assetJson, customTs, simThemeJson } = computeBodyMetadata(body);
118936
118704
  // noDiffs legacy
118937
118705
  if (metadata.diffs === true // enabled in tutorial
118938
118706
  || (metadata.diffs !== false && metadata.noDiffs !== true // not disabled
@@ -118958,6 +118726,7 @@ var pxt;
118958
118726
  activities,
118959
118727
  code,
118960
118728
  templateCode,
118729
+ templateLanguage,
118961
118730
  metadata,
118962
118731
  language,
118963
118732
  jres,
@@ -118970,7 +118739,7 @@ var pxt;
118970
118739
  }
118971
118740
  tutorial_1.parseTutorial = parseTutorial;
118972
118741
  function getMetadataRegex() {
118973
- return /``` *(sim|block|blocks|filterblocks|spy|ghost|typescript|ts|js|javascript|template|python|jres|assetjson|customts|simtheme)\s*\n([\s\S]*?)\n```/gmi;
118742
+ return /``` *(sim|block|blocks|filterblocks|spy|ghost|typescript|ts|js|javascript|template|python|jres|assetjson|customts|simtheme|python-template|ts-template|typescript-template|js-template|javascript-template)\s*\n([\s\S]*?)\n```/gmi;
118974
118743
  }
118975
118744
  tutorial_1.getMetadataRegex = getMetadataRegex;
118976
118745
  function computeBodyMetadata(body) {
@@ -118980,6 +118749,7 @@ var pxt;
118980
118749
  let jres;
118981
118750
  let code = [];
118982
118751
  let templateCode;
118752
+ let templateLanguage;
118983
118753
  let language;
118984
118754
  let idx = 0;
118985
118755
  let assetJson;
@@ -119015,6 +118785,22 @@ var pxt;
119015
118785
  case "template":
119016
118786
  templateCode = m2;
119017
118787
  break;
118788
+ case "python-template":
118789
+ if (!checkTutorialEditor(pxt.PYTHON_PROJECT_NAME))
118790
+ return undefined;
118791
+ templateCode = m2;
118792
+ templateLanguage = "python";
118793
+ language = "python";
118794
+ break;
118795
+ case "ts-template":
118796
+ case "typescript-template":
118797
+ case "js-template":
118798
+ case "javascript-template":
118799
+ if (!checkTutorialEditor(pxt.JAVASCRIPT_PROJECT_NAME))
118800
+ return undefined;
118801
+ templateCode = m2;
118802
+ templateLanguage = "typescript";
118803
+ break;
119018
118804
  case "jres":
119019
118805
  jres = m2;
119020
118806
  break;
@@ -119038,6 +118824,7 @@ var pxt;
119038
118824
  return {
119039
118825
  code,
119040
118826
  templateCode,
118827
+ templateLanguage,
119041
118828
  editor,
119042
118829
  language,
119043
118830
  jres,
@@ -119320,6 +119107,7 @@ ${code}
119320
119107
  tutorialCode: tutorialInfo.code,
119321
119108
  tutorialRecipe: !!recipe,
119322
119109
  templateCode: tutorialInfo.templateCode,
119110
+ templateLanguage: tutorialInfo.templateLanguage,
119323
119111
  autoexpandStep: ((_a = tutorialInfo.metadata) === null || _a === void 0 ? void 0 : _a.autoexpandOff) ? false : true,
119324
119112
  metadata: tutorialInfo.metadata,
119325
119113
  language: tutorialInfo.language,
@@ -160874,7 +160662,7 @@ function checkIfTaggedCommitAsync() {
160874
160662
  });
160875
160663
  }
160876
160664
  let readJson = nodeutil.readJson;
160877
- function ciAsync() {
160665
+ async function ciAsync() {
160878
160666
  forceCloudBuild = true;
160879
160667
  const buildInfo = ciBuildInfo();
160880
160668
  pxt.log(`ci build using ${buildInfo.ci}`);
@@ -160920,55 +160708,54 @@ function ciAsync() {
160920
160708
  let pkg = readJson("package.json");
160921
160709
  if (pkg["name"] == "pxt-core") {
160922
160710
  pxt.log("pxt-core build");
160923
- return checkIfTaggedCommitAsync()
160924
- .then(isTaggedCommit => {
160925
- pxt.log(`is tagged commit: ${isTaggedCommit}`);
160926
- let p = npmPublishAsync();
160927
- if (branch === "master" && isTaggedCommit) {
160928
- if (uploadDocs)
160929
- p = p
160930
- .then(() => buildWebStringsAsync())
160931
- .then(() => crowdin.execCrowdinAsync("upload", "built/webstrings.json"))
160932
- .then(() => crowdin.execCrowdinAsync("upload", "built/skillmap-strings.json"))
160933
- .then(() => crowdin.execCrowdinAsync("upload", "built/authcode-strings.json"))
160934
- .then(() => crowdin.execCrowdinAsync("upload", "built/multiplayer-strings.json"))
160935
- .then(() => crowdin.execCrowdinAsync("upload", "built/kiosk-strings.json"));
160936
- if (uploadApiStrings)
160937
- p = p.then(() => crowdin.execCrowdinAsync("upload", "built/strings.json"));
160938
- if (uploadDocs || uploadApiStrings)
160939
- p = p.then(() => crowdin.internalUploadTargetTranslationsAsync(uploadApiStrings, uploadDocs));
160711
+ const isTaggedCommit = await checkIfTaggedCommitAsync();
160712
+ pxt.log(`is tagged commit: ${isTaggedCommit}`);
160713
+ await npmPublishAsync();
160714
+ if (branch === "master" && isTaggedCommit) {
160715
+ if (uploadDocs) {
160716
+ await buildWebStringsAsync();
160717
+ await crowdin.uploadBuiltStringsAsync("built/webstrings.json");
160718
+ await crowdin.uploadBuiltStringsAsync(`built/skillmap-strings.json`);
160719
+ await crowdin.uploadBuiltStringsAsync(`built/authcode-strings.json`);
160720
+ await crowdin.uploadBuiltStringsAsync(`built/multiplayer-strings.json`);
160721
+ await crowdin.uploadBuiltStringsAsync(`built/kiosk-strings.json`);
160722
+ }
160723
+ if (uploadApiStrings) {
160724
+ await crowdin.uploadBuiltStringsAsync("built/strings.json");
160940
160725
  }
160941
- return p;
160942
- });
160726
+ if (uploadDocs || uploadApiStrings) {
160727
+ await crowdin.internalUploadTargetTranslationsAsync(uploadApiStrings, uploadDocs);
160728
+ pxt.log("translations uploaded");
160729
+ }
160730
+ else {
160731
+ pxt.log("skipping translations upload");
160732
+ }
160733
+ }
160943
160734
  }
160944
160735
  else {
160945
160736
  pxt.log("target build");
160946
- return internalBuildTargetAsync()
160947
- .then(() => internalCheckDocsAsync(true))
160948
- .then(() => blockTestsAsync())
160949
- .then(() => npmPublishAsync())
160950
- .then(() => {
160951
- if (!process.env["PXT_ACCESS_TOKEN"]) {
160952
- // pull request, don't try to upload target
160953
- pxt.log('no token, skipping upload');
160954
- return Promise.resolve();
160955
- }
160737
+ await internalBuildTargetAsync();
160738
+ await internalCheckDocsAsync(true);
160739
+ await blockTestsAsync();
160740
+ await npmPublishAsync();
160741
+ if (!process.env["PXT_ACCESS_TOKEN"]) {
160742
+ // pull request, don't try to upload target
160743
+ pxt.log('no token, skipping upload');
160744
+ }
160745
+ else {
160956
160746
  const trg = readLocalPxTarget();
160957
160747
  const label = `${trg.id}/${tag || latest}`;
160958
160748
  pxt.log(`uploading target with label ${label}...`);
160959
- return uploadTargetAsync(label);
160960
- })
160961
- .then(() => {
160962
- pxt.log("target uploaded");
160963
- if (uploadDocs || uploadApiStrings) {
160964
- return crowdin.internalUploadTargetTranslationsAsync(uploadApiStrings, uploadDocs)
160965
- .then(() => pxt.log("translations uploaded"));
160966
- }
160967
- else {
160968
- pxt.log("skipping translations upload");
160969
- return Promise.resolve();
160970
- }
160971
- });
160749
+ await uploadTargetAsync(label);
160750
+ }
160751
+ pxt.log("target uploaded");
160752
+ if (uploadDocs || uploadApiStrings) {
160753
+ await crowdin.internalUploadTargetTranslationsAsync(uploadApiStrings, uploadDocs);
160754
+ pxt.log("translations uploaded");
160755
+ }
160756
+ else {
160757
+ pxt.log("skipping translations upload");
160758
+ }
160972
160759
  }
160973
160760
  }
160974
160761
  function lintJSONInDirectory(dir) {
@@ -166839,7 +166626,7 @@ ${pxt.crowdin.KEY_VARIABLE} - crowdin key
166839
166626
  advanced: true,
166840
166627
  }, pc => uploadTargetRefsAsync(pc.args[0]));
166841
166628
  advancedCommand("uploadtt", "upload tagged release", uploadTaggedTargetAsync, "");
166842
- advancedCommand("downloadtrgtranslations", "download translations from bundled projects", crowdin.downloadTargetTranslationsAsync, "<package>");
166629
+ advancedCommand("downloadtrgtranslations", "download translations from bundled projects", crowdin.downloadTargetTranslationsAsync, "[package]");
166843
166630
  p.defineCommand({
166844
166631
  name: "checkdocs",
166845
166632
  onlineHelp: true,
@@ -166917,7 +166704,19 @@ ${pxt.crowdin.KEY_VARIABLE} - crowdin key
166917
166704
  }
166918
166705
  }, buildAuthcodeAsync);
166919
166706
  advancedCommand("augmentdocs", "test markdown docs replacements", augmnetDocsAsync, "<temlate.md> <doc.md>");
166920
- advancedCommand("crowdin", "upload, download, clean, stats files to/from crowdin", pc => crowdin.execCrowdinAsync.apply(undefined, pc.args), "<cmd> <path> [output]");
166707
+ p.defineCommand({
166708
+ name: "crowdin",
166709
+ advanced: true,
166710
+ argString: "<cmd> <path> [output]",
166711
+ help: "upload, download, clean, stats files to/from crowdin",
166712
+ flags: {
166713
+ test: { description: "test run, do not upload files to crowdin" }
166714
+ }
166715
+ }, pc => {
166716
+ if (pc.flags.test)
166717
+ pxt.crowdin.setTestMode();
166718
+ return crowdin.execCrowdinAsync.apply(undefined, pc.args);
166719
+ });
166921
166720
  advancedCommand("hidlist", "list HID devices", hid.listAsync);
166922
166721
  advancedCommand("hidserial", "run HID serial forwarding", hid.serialAsync, undefined, true);
166923
166722
  advancedCommand("hiddmesg", "fetch DMESG buffer over HID and print it", hid.dmesgAsync, undefined, true);
@@ -12159,7 +12159,7 @@ var pxtblockly;
12159
12159
  const existing = pxt.lookupProjectAssetByTSReference(text, project);
12160
12160
  if (existing)
12161
12161
  return existing;
12162
- const frames = parseImageArrayString(text);
12162
+ const frames = parseImageArrayString(text, this.params.taggedTemplate);
12163
12163
  if (frames && frames.length) {
12164
12164
  const id = this.sourceBlock_.id;
12165
12165
  const newAnimation = {
@@ -12195,7 +12195,7 @@ var pxtblockly;
12195
12195
  if (!this.asset)
12196
12196
  return "[]";
12197
12197
  if (this.isTemporaryAsset()) {
12198
- return "[" + this.asset.frames.map(frame => pxt.sprite.bitmapToImageLiteral(pxt.sprite.Bitmap.fromData(frame), "typescript" /* pxt.editor.FileType.TypeScript */)).join(",") + "]";
12198
+ return "[" + this.asset.frames.map(frame => pxt.sprite.bitmapToImageLiteral(pxt.sprite.Bitmap.fromData(frame), "typescript" /* pxt.editor.FileType.TypeScript */, this.params.taggedTemplate)).join(",") + "]";
12199
12199
  }
12200
12200
  return pxt.getTSReferenceForAsset(this.asset);
12201
12201
  }
@@ -12274,6 +12274,7 @@ var pxtblockly;
12274
12274
  }
12275
12275
  parsed.initWidth = withDefault(opts.initWidth, parsed.initWidth);
12276
12276
  parsed.initHeight = withDefault(opts.initHeight, parsed.initHeight);
12277
+ parsed.taggedTemplate = opts.taggedTemplate;
12277
12278
  return parsed;
12278
12279
  function withDefault(raw, def) {
12279
12280
  const res = parseInt(raw);
@@ -12283,11 +12284,11 @@ var pxtblockly;
12283
12284
  return res;
12284
12285
  }
12285
12286
  }
12286
- function parseImageArrayString(str) {
12287
+ function parseImageArrayString(str, templateLiteral) {
12287
12288
  if (str.indexOf("[") === -1)
12288
12289
  return null;
12289
12290
  str = str.replace(/[\[\]]/mg, "");
12290
- return str.split(",").map(s => pxt.sprite.imageLiteralToBitmap(s).data()).filter(b => b.height && b.width);
12291
+ return str.split(",").map(s => pxt.sprite.imageLiteralToBitmap(s, templateLiteral).data()).filter(b => b.height && b.width);
12291
12292
  }
12292
12293
  function isNumberType(type) {
12293
12294
  return type === "math_number" || type === "math_integer" || type === "math_whole_number";
@@ -16326,7 +16327,7 @@ var pxtblockly;
16326
16327
  if (this.getBlockData()) {
16327
16328
  return project.lookupAsset("image" /* pxt.AssetType.Image */, this.getBlockData());
16328
16329
  }
16329
- const bmp = text ? pxt.sprite.imageLiteralToBitmap(text) : new pxt.sprite.Bitmap(this.params.initWidth, this.params.initHeight);
16330
+ const bmp = text ? pxt.sprite.imageLiteralToBitmap(text, this.params.taggedTemplate) : new pxt.sprite.Bitmap(this.params.initWidth, this.params.initHeight);
16330
16331
  if (!bmp) {
16331
16332
  this.isGreyBlock = true;
16332
16333
  this.valueText = text;
@@ -16347,7 +16348,7 @@ var pxtblockly;
16347
16348
  if (this.asset && !this.isTemporaryAsset()) {
16348
16349
  return pxt.getTSReferenceForAsset(this.asset);
16349
16350
  }
16350
- return pxt.sprite.bitmapToImageLiteral(this.asset && pxt.sprite.Bitmap.fromData(this.asset.bitmap), "typescript" /* pxt.editor.FileType.TypeScript */);
16351
+ return pxt.sprite.bitmapToImageLiteral(this.asset && pxt.sprite.Bitmap.fromData(this.asset.bitmap), "typescript" /* pxt.editor.FileType.TypeScript */, this.params.taggedTemplate);
16351
16352
  }
16352
16353
  parseFieldOptions(opts) {
16353
16354
  return parseFieldOptions(opts);
@@ -16402,6 +16403,7 @@ var pxtblockly;
16402
16403
  parsed.initColor = withDefault(opts.initColor, parsed.initColor);
16403
16404
  parsed.initWidth = withDefault(opts.initWidth, parsed.initWidth);
16404
16405
  parsed.initHeight = withDefault(opts.initHeight, parsed.initHeight);
16406
+ parsed.taggedTemplate = opts.taggedTemplate;
16405
16407
  return parsed;
16406
16408
  function withDefault(raw, def) {
16407
16409
  const res = parseInt(raw);
@@ -498,6 +498,7 @@ declare namespace pxtblockly {
498
498
  disableResize: string;
499
499
  filter?: string;
500
500
  lightMode: boolean;
501
+ taggedTemplate?: string;
501
502
  }
502
503
  interface ParsedFieldAnimationOptions {
503
504
  initWidth: number;
@@ -505,6 +506,7 @@ declare namespace pxtblockly {
505
506
  disableResize: boolean;
506
507
  filter?: string;
507
508
  lightMode: boolean;
509
+ taggedTemplate?: string;
508
510
  }
509
511
  class FieldAnimationEditor extends FieldAssetEditor<FieldAnimationOptions, ParsedFieldAnimationOptions> {
510
512
  protected frames: string[];
@@ -1293,6 +1295,7 @@ declare namespace pxtblockly {
1293
1295
  disableResize: string;
1294
1296
  filter?: string;
1295
1297
  lightMode: boolean;
1298
+ taggedTemplate?: string;
1296
1299
  }
1297
1300
  interface ParsedSpriteEditorOptions {
1298
1301
  initColor: number;
@@ -1301,6 +1304,7 @@ declare namespace pxtblockly {
1301
1304
  disableResize: boolean;
1302
1305
  filter?: string;
1303
1306
  lightMode: boolean;
1307
+ taggedTemplate?: string;
1304
1308
  }
1305
1309
  export class FieldSpriteEditor extends FieldAssetEditor<FieldSpriteEditorOptions, ParsedSpriteEditorOptions> {
1306
1310
  protected getAssetType(): pxt.AssetType;