pxt-core 13.2.16 → 13.2.17

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.
@@ -572,6 +572,8 @@ const lf = ts.pxtc.Util.lf;
572
572
  let parts = k.split(".");
573
573
  for (let i = 0; i < parts.length; ++i) {
574
574
  let part = parts[i];
575
+ if (part === "__proto__" || part === "constructor" || part === "prototype")
576
+ Util.userError("Invalid configuration key: " + k);
575
577
  if (i == parts.length - 1)
576
578
  ptr[part] = v[k];
577
579
  else {
@@ -1416,12 +1418,20 @@ ${opts.repo.name.replace(/^pxt-/, '')}=github:${opts.repo.fullName}#${opts.repo.
1416
1418
  strong: 1,
1417
1419
  em: 1,
1418
1420
  };
1421
+ /** Keeps metadata and token-only HTML out of the translation catalog. */
1422
+ function isNonTranslatable(text) {
1423
+ if (text.startsWith("IE=edge,") || text.startsWith("width=device-width"))
1424
+ return true;
1425
+ // Remove URLs, template placeholders, and spacing entities to look for prose.
1426
+ const prose = text.replace(/(?:https?:\/\/|\/)[\w@\/.]+|@[-\w]+@|\{[^{}]+\}|&nbsp;/g, "");
1427
+ return !/[a-zA-Z]/.test(prose);
1428
+ }
1419
1429
  function translate(html, locale) {
1420
1430
  const missing = {};
1421
1431
  function translateOne(toTranslate) {
1422
1432
  let spm = /^(\s*)([^]*?)(\s*)$/.exec(toTranslate);
1423
1433
  let text = spm[2].replace(/\s+/g, " ");
1424
- if (text == "" || /^((IE=edge,.*|width=device-width.*|(https?:\/\/|\/)[\w@\/\.]+|@[\-\w]+@|\{[^\{\}]+\}|[^a-zA-Z]*|(&nbsp;)+)\s*)+$/.test(text))
1434
+ if (isNonTranslatable(text))
1425
1435
  return null;
1426
1436
  let v = U.lookup(locale, text);
1427
1437
  if (v)
@@ -3056,10 +3066,11 @@ var pxt;
3056
3066
  if (!inEnum)
3057
3067
  return;
3058
3068
  // parse the enum case, with lots of optional stuff (?)
3059
- let mm = /^\s*(\w+)\s*(=\s*(.*?))?,?\s*$/.exec(lnNC);
3069
+ const enumLine = lnNC.trim().replace(/,$/, "").trim();
3070
+ let mm = /^(\w+)(?:\s*=(.*))?$/.exec(enumLine);
3060
3071
  if (mm) {
3061
3072
  let nm = mm[1];
3062
- let v = mm[3];
3073
+ let v = mm[2] && mm[2].trim();
3063
3074
  let opt = "";
3064
3075
  if (v) {
3065
3076
  // user-supplied value
@@ -3097,23 +3108,26 @@ var pxt;
3097
3108
  }
3098
3109
  function parseArg(parsedAttrs, s) {
3099
3110
  s = s.trim();
3100
- let m = /(.*)=\s*(-?\w+)$/.exec(s);
3111
+ const equals = s.lastIndexOf("=");
3112
+ const m = equals < 0 ? null : /^\s*(-?\w+)$/.exec(s.slice(equals + 1));
3101
3113
  let defl = "";
3102
3114
  let qm = "";
3103
3115
  if (m) {
3104
- defl = m[2];
3116
+ defl = m[1];
3105
3117
  qm = "?";
3106
- s = m[1].trim();
3118
+ s = s.slice(0, equals).trim();
3107
3119
  }
3108
- m = /^(.*?)(\w+)$/.exec(s);
3109
- if (!m) {
3120
+ let nameStart = s.length;
3121
+ while (nameStart > 0 && /\w/.test(s.charAt(nameStart - 1)))
3122
+ --nameStart;
3123
+ if (nameStart == s.length || /[\r\n\u2028\u2029]/.test(s.slice(0, nameStart))) {
3110
3124
  err("invalid argument: " + s);
3111
3125
  return {
3112
3126
  name: "???",
3113
3127
  type: "int"
3114
3128
  };
3115
3129
  }
3116
- let argName = m[2];
3130
+ let argName = s.slice(nameStart);
3117
3131
  if (parsedAttrs.paramDefl[argName]) {
3118
3132
  defl = parsedAttrs.paramDefl[argName];
3119
3133
  qm = "?";
@@ -3128,7 +3142,7 @@ var pxt;
3128
3142
  }
3129
3143
  return {
3130
3144
  name: argName + qm,
3131
- type: m[1]
3145
+ type: s.slice(0, nameStart)
3132
3146
  };
3133
3147
  }
3134
3148
  function parseCpp(src, isHeader) {
@@ -3138,9 +3152,28 @@ var pxt;
3138
3152
  inDocComment = false;
3139
3153
  let indexedInstanceAttrs;
3140
3154
  let indexedInstanceIdx = -1;
3141
- // replace #if 0 .... #endif with newlines
3142
- src = src.replace(/^(\s*#\s*if\s+(\w+)\s*$)([^]*?)(^\s*#\s*(elif|else|endif)\s*$)/mg, (f, _if, arg, middle, _endif) => hash_if_options.indexOf(arg) >= 0 && !cpp_options[arg] ?
3143
- _if + middle.replace(/[^\n]/g, "") + _endif : f);
3155
+ // Match directives one line at a time so unterminated blocks cannot rescan the source.
3156
+ const lines = src.split(/\r?\n/);
3157
+ let ifStart = -1;
3158
+ let ifArg = "";
3159
+ lines.forEach((ln, i) => {
3160
+ const directive = /^#\s*(?:if\s+(\w+)|(elif|else|endif))$/.exec(ln.trim());
3161
+ if (!directive)
3162
+ return;
3163
+ if (ifStart < 0) {
3164
+ if (directive[1]) {
3165
+ ifStart = i;
3166
+ ifArg = directive[1];
3167
+ }
3168
+ }
3169
+ else if (directive[2]) {
3170
+ if (hash_if_options.indexOf(ifArg) >= 0 && !cpp_options[ifArg]) {
3171
+ for (let j = ifStart + 1; j < i; ++j)
3172
+ lines[j] = "";
3173
+ }
3174
+ ifStart = -1;
3175
+ }
3176
+ });
3144
3177
  // special handling of C++ namespace that ends with Methods (e.g. FooMethods)
3145
3178
  // such a namespace will be converted into a TypeScript interface
3146
3179
  // this enables simple objects with methods to be defined. See, for example:
@@ -3218,10 +3251,14 @@ var pxt;
3218
3251
  return "T";
3219
3252
  case "String":
3220
3253
  return "S";
3221
- default:
3254
+ default: {
3222
3255
  if (U.lookup(knownEnums, tp))
3223
3256
  return "I";
3224
- return "_" + tp.replace(/[\*_]+$/, "");
3257
+ let typeEnd = tp.length;
3258
+ while (typeEnd > 0 && /[\*_]/.test(tp.charAt(typeEnd - 1)))
3259
+ --typeEnd;
3260
+ return "_" + tp.slice(0, typeEnd);
3261
+ }
3225
3262
  }
3226
3263
  }
3227
3264
  function generateVMWrapper(fi, argTypes) {
@@ -3303,7 +3340,7 @@ var pxt;
3303
3340
  enumVal = 0;
3304
3341
  enumsDTS.setNs("");
3305
3342
  shimsDTS.setNs("");
3306
- src.split(/\r?\n/).forEach(ln => {
3343
+ lines.forEach(ln => {
3307
3344
  ++lineNo;
3308
3345
  // remove comments (NC = no comments)
3309
3346
  let lnNC = stripComments(ln);
package/built/cli.js CHANGED
@@ -618,10 +618,14 @@ async function bumpAsync(parsed) {
618
618
  }
619
619
  catch (e) {
620
620
  console.warn(chalk.yellow("Unable to determine branch protection status."), e.message);
621
+ pr = true;
621
622
  }
622
623
  if (nopr) {
623
624
  pr = false;
624
625
  }
626
+ if (pr && (!currBranchName || !token || !user || !owner || !repo)) {
627
+ U.userError("Unable to prepare a bump pull request. Check GitHub credentials and the origin URL, or explicitly use --nopr for a direct push.");
628
+ }
625
629
  if (fs.existsSync(pxt.CONFIG_NAME)) {
626
630
  if (upload) {
627
631
  U.userError("upload only supported on targets");
@@ -634,7 +638,7 @@ async function bumpAsync(parsed) {
634
638
  .then(() => nodeutil.runGitAsync("pull"))
635
639
  .then(() => nodeutil.createBranchAsync(newBranchName))
636
640
  .then(() => justBumpPkgAsync(bumpType, false)) // don't tag when creating a PR
637
- .then((version) => nodeutil.gitPushAsync().then(() => version))
641
+ .then((version) => nodeutil.gitPushAsync(false).then(() => version))
638
642
  .then((version) => nodeutil.createPullRequestAsync({
639
643
  title: `[pxt-cli] bump version to ${version}`,
640
644
  body: "__Do not edit the PR title.__\n" +
@@ -654,8 +658,7 @@ async function bumpAsync(parsed) {
654
658
  return Promise.resolve()
655
659
  .then(() => nodeutil.runGitAsync("pull"))
656
660
  .then(() => justBumpPkgAsync(bumpType))
657
- .then(() => nodeutil.runGitAsync("push", "--follow-tags"))
658
- .then(() => nodeutil.runGitAsync("push"));
661
+ .then(() => nodeutil.runGitAsync("push", "--atomic", "--follow-tags"));
659
662
  }
660
663
  }
661
664
  else if (fs.existsSync("pxtarget.json"))
@@ -668,7 +671,7 @@ async function bumpAsync(parsed) {
668
671
  .then(() => nodeutil.createBranchAsync(newBranchName))
669
672
  .then(() => bumpPxt ? bumpPxtCoreDepAsync() : Promise.resolve())
670
673
  .then(() => nodeutil.npmVersionBumpAsync(bumpType, false)) // don't tag when creating a PR
671
- .then((version) => nodeutil.gitPushAsync().then(() => version))
674
+ .then((version) => nodeutil.gitPushAsync(false).then(() => version))
672
675
  .then((version) => nodeutil.createPullRequestAsync({
673
676
  title: `[pxt-cli] bump version to ${version}`,
674
677
  body: "__Do not edit the PR title.__\n" +
@@ -687,10 +690,9 @@ async function bumpAsync(parsed) {
687
690
  console.log("Bumping via direct push.");
688
691
  return Promise.resolve()
689
692
  .then(() => nodeutil.runGitAsync("pull"))
690
- .then(() => bumpPxt ? bumpPxtCoreDepAsync().then(() => nodeutil.runGitAsync("push")) : Promise.resolve())
693
+ .then(() => bumpPxt ? bumpPxtCoreDepAsync().then(() => nodeutil.runGitAsync("push", "--atomic", "--no-follow-tags")) : Promise.resolve())
691
694
  .then(() => nodeutil.runNpmAsync("version", bumpType))
692
- .then(() => nodeutil.runGitAsync("push", "--follow-tags"))
693
- .then(() => nodeutil.runGitAsync("push"))
695
+ .then(() => nodeutil.runGitAsync("push", "--atomic", "--follow-tags"))
694
696
  .then(() => upload ? uploadTaggedTargetAsync() : Promise.resolve());
695
697
  }
696
698
  else {
@@ -4701,7 +4703,7 @@ async function staticpkgAsync(parsed) {
4701
4703
  pxt.log(`packaging editor to ${builtPackaged}`);
4702
4704
  await rimrafAsync(builtPackaged, {});
4703
4705
  if (bump) {
4704
- await bumpAsync();
4706
+ await bumpAsync(parsed);
4705
4707
  }
4706
4708
  if (locs) {
4707
4709
  await internalGenDocsAsync(false, true);
@@ -6552,7 +6554,7 @@ ${pxt.crowdin.KEY_VARIABLE} - crowdin key
6552
6554
  update: { description: "(package only) Updates pxt-core reference to the latest release" },
6553
6555
  upload: { description: "(package only) Upload after bumping" },
6554
6556
  pr: { description: "Bump via pull request rather than direct push" },
6555
- nopr: { description: "Bump via direct push rather than pull request (overrides --pr)" },
6557
+ nopr: { description: "Force a direct bump (overrides --pr and protection detection)" },
6556
6558
  version: {
6557
6559
  description: "Which part of the version to bump, or a complete version number to assign. Defaults to 'patch'",
6558
6560
  argument: "version",
@@ -6670,6 +6672,9 @@ ${pxt.crowdin.KEY_VARIABLE} - crowdin key
6670
6672
  bump: {
6671
6673
  description: "bump version number prior to package"
6672
6674
  },
6675
+ nopr: {
6676
+ description: "With --bump, force a direct bump rather than a pull request"
6677
+ },
6673
6678
  cloudbuild: {
6674
6679
  description: "(deprecated) forces build to happen in the cloud",
6675
6680
  aliases: ["cloud", "cloud-build", "cb"]
package/built/nodeutil.js CHANGED
@@ -219,7 +219,7 @@ async function getGitHubOwnerAndRepoAsync() {
219
219
  if (!remoteUrl) {
220
220
  Util.userError("No remote origin URL found");
221
221
  }
222
- const match = remoteUrl.match(/github\.com[:\/](.+?)\/(.+?)(\.git)?$/);
222
+ const match = remoteUrl.replace(/\/+$/, "").match(/github\.com[:\/](.+?)\/(.+?)(\.git)?$/);
223
223
  if (!match) {
224
224
  Util.userError("Invalid remote origin URL: " + remoteUrl);
225
225
  }
@@ -236,7 +236,7 @@ async function createBranchAsync(branchName) {
236
236
  });
237
237
  await spawnAsync({
238
238
  cmd: "git",
239
- args: ["push", "--set-upstream", "origin", branchName],
239
+ args: ["push", "--no-follow-tags", "--set-upstream", "origin", branchName],
240
240
  silent: true,
241
241
  });
242
242
  }
@@ -299,13 +299,9 @@ function quoteIfNeeded(arg) {
299
299
  return arg;
300
300
  }
301
301
  function gitPushAsync(followTags = true) {
302
- const args = ["push"];
303
- if (followTags)
304
- args.push("--follow-tags");
305
- args.push("origin", "HEAD");
306
302
  return spawnAsync({
307
303
  cmd: "git",
308
- args,
304
+ args: ["push", "--atomic", followTags ? "--follow-tags" : "--no-follow-tags", "origin", "HEAD"],
309
305
  cwd: ".",
310
306
  silent: true,
311
307
  });
package/built/pxt.js CHANGED
@@ -99231,6 +99231,8 @@ const lf = ts.pxtc.Util.lf;
99231
99231
  let parts = k.split(".");
99232
99232
  for (let i = 0; i < parts.length; ++i) {
99233
99233
  let part = parts[i];
99234
+ if (part === "__proto__" || part === "constructor" || part === "prototype")
99235
+ Util.userError("Invalid configuration key: " + k);
99234
99236
  if (i == parts.length - 1)
99235
99237
  ptr[part] = v[k];
99236
99238
  else {
@@ -105525,10 +105527,11 @@ var pxt;
105525
105527
  if (!inEnum)
105526
105528
  return;
105527
105529
  // parse the enum case, with lots of optional stuff (?)
105528
- let mm = /^\s*(\w+)\s*(=\s*(.*?))?,?\s*$/.exec(lnNC);
105530
+ const enumLine = lnNC.trim().replace(/,$/, "").trim();
105531
+ let mm = /^(\w+)(?:\s*=(.*))?$/.exec(enumLine);
105529
105532
  if (mm) {
105530
105533
  let nm = mm[1];
105531
- let v = mm[3];
105534
+ let v = mm[2] && mm[2].trim();
105532
105535
  let opt = "";
105533
105536
  if (v) {
105534
105537
  // user-supplied value
@@ -105566,23 +105569,26 @@ var pxt;
105566
105569
  }
105567
105570
  function parseArg(parsedAttrs, s) {
105568
105571
  s = s.trim();
105569
- let m = /(.*)=\s*(-?\w+)$/.exec(s);
105572
+ const equals = s.lastIndexOf("=");
105573
+ const m = equals < 0 ? null : /^\s*(-?\w+)$/.exec(s.slice(equals + 1));
105570
105574
  let defl = "";
105571
105575
  let qm = "";
105572
105576
  if (m) {
105573
- defl = m[2];
105577
+ defl = m[1];
105574
105578
  qm = "?";
105575
- s = m[1].trim();
105579
+ s = s.slice(0, equals).trim();
105576
105580
  }
105577
- m = /^(.*?)(\w+)$/.exec(s);
105578
- if (!m) {
105581
+ let nameStart = s.length;
105582
+ while (nameStart > 0 && /\w/.test(s.charAt(nameStart - 1)))
105583
+ --nameStart;
105584
+ if (nameStart == s.length || /[\r\n\u2028\u2029]/.test(s.slice(0, nameStart))) {
105579
105585
  err("invalid argument: " + s);
105580
105586
  return {
105581
105587
  name: "???",
105582
105588
  type: "int"
105583
105589
  };
105584
105590
  }
105585
- let argName = m[2];
105591
+ let argName = s.slice(nameStart);
105586
105592
  if (parsedAttrs.paramDefl[argName]) {
105587
105593
  defl = parsedAttrs.paramDefl[argName];
105588
105594
  qm = "?";
@@ -105597,7 +105603,7 @@ var pxt;
105597
105603
  }
105598
105604
  return {
105599
105605
  name: argName + qm,
105600
- type: m[1]
105606
+ type: s.slice(0, nameStart)
105601
105607
  };
105602
105608
  }
105603
105609
  function parseCpp(src, isHeader) {
@@ -105607,9 +105613,28 @@ var pxt;
105607
105613
  inDocComment = false;
105608
105614
  let indexedInstanceAttrs;
105609
105615
  let indexedInstanceIdx = -1;
105610
- // replace #if 0 .... #endif with newlines
105611
- src = src.replace(/^(\s*#\s*if\s+(\w+)\s*$)([^]*?)(^\s*#\s*(elif|else|endif)\s*$)/mg, (f, _if, arg, middle, _endif) => hash_if_options.indexOf(arg) >= 0 && !cpp_options[arg] ?
105612
- _if + middle.replace(/[^\n]/g, "") + _endif : f);
105616
+ // Match directives one line at a time so unterminated blocks cannot rescan the source.
105617
+ const lines = src.split(/\r?\n/);
105618
+ let ifStart = -1;
105619
+ let ifArg = "";
105620
+ lines.forEach((ln, i) => {
105621
+ const directive = /^#\s*(?:if\s+(\w+)|(elif|else|endif))$/.exec(ln.trim());
105622
+ if (!directive)
105623
+ return;
105624
+ if (ifStart < 0) {
105625
+ if (directive[1]) {
105626
+ ifStart = i;
105627
+ ifArg = directive[1];
105628
+ }
105629
+ }
105630
+ else if (directive[2]) {
105631
+ if (hash_if_options.indexOf(ifArg) >= 0 && !cpp_options[ifArg]) {
105632
+ for (let j = ifStart + 1; j < i; ++j)
105633
+ lines[j] = "";
105634
+ }
105635
+ ifStart = -1;
105636
+ }
105637
+ });
105613
105638
  // special handling of C++ namespace that ends with Methods (e.g. FooMethods)
105614
105639
  // such a namespace will be converted into a TypeScript interface
105615
105640
  // this enables simple objects with methods to be defined. See, for example:
@@ -105687,10 +105712,14 @@ var pxt;
105687
105712
  return "T";
105688
105713
  case "String":
105689
105714
  return "S";
105690
- default:
105715
+ default: {
105691
105716
  if (U.lookup(knownEnums, tp))
105692
105717
  return "I";
105693
- return "_" + tp.replace(/[\*_]+$/, "");
105718
+ let typeEnd = tp.length;
105719
+ while (typeEnd > 0 && /[\*_]/.test(tp.charAt(typeEnd - 1)))
105720
+ --typeEnd;
105721
+ return "_" + tp.slice(0, typeEnd);
105722
+ }
105694
105723
  }
105695
105724
  }
105696
105725
  function generateVMWrapper(fi, argTypes) {
@@ -105772,7 +105801,7 @@ var pxt;
105772
105801
  enumVal = 0;
105773
105802
  enumsDTS.setNs("");
105774
105803
  shimsDTS.setNs("");
105775
- src.split(/\r?\n/).forEach(ln => {
105804
+ lines.forEach(ln => {
105776
105805
  ++lineNo;
105777
105806
  // remove comments (NC = no comments)
105778
105807
  let lnNC = stripComments(ln);
@@ -124958,12 +124987,20 @@ ${opts.repo.name.replace(/^pxt-/, '')}=github:${opts.repo.fullName}#${opts.repo.
124958
124987
  strong: 1,
124959
124988
  em: 1,
124960
124989
  };
124990
+ /** Keeps metadata and token-only HTML out of the translation catalog. */
124991
+ function isNonTranslatable(text) {
124992
+ if (text.startsWith("IE=edge,") || text.startsWith("width=device-width"))
124993
+ return true;
124994
+ // Remove URLs, template placeholders, and spacing entities to look for prose.
124995
+ const prose = text.replace(/(?:https?:\/\/|\/)[\w@\/.]+|@[-\w]+@|\{[^{}]+\}|&nbsp;/g, "");
124996
+ return !/[a-zA-Z]/.test(prose);
124997
+ }
124961
124998
  function translate(html, locale) {
124962
124999
  const missing = {};
124963
125000
  function translateOne(toTranslate) {
124964
125001
  let spm = /^(\s*)([^]*?)(\s*)$/.exec(toTranslate);
124965
125002
  let text = spm[2].replace(/\s+/g, " ");
124966
- if (text == "" || /^((IE=edge,.*|width=device-width.*|(https?:\/\/|\/)[\w@\/\.]+|@[\-\w]+@|\{[^\{\}]+\}|[^a-zA-Z]*|(&nbsp;)+)\s*)+$/.test(text))
125003
+ if (isNonTranslatable(text))
124967
125004
  return null;
124968
125005
  let v = U.lookup(locale, text);
124969
125006
  if (v)
@@ -165904,10 +165941,14 @@ async function bumpAsync(parsed) {
165904
165941
  }
165905
165942
  catch (e) {
165906
165943
  console.warn(chalk.yellow("Unable to determine branch protection status."), e.message);
165944
+ pr = true;
165907
165945
  }
165908
165946
  if (nopr) {
165909
165947
  pr = false;
165910
165948
  }
165949
+ if (pr && (!currBranchName || !token || !user || !owner || !repo)) {
165950
+ U.userError("Unable to prepare a bump pull request. Check GitHub credentials and the origin URL, or explicitly use --nopr for a direct push.");
165951
+ }
165911
165952
  if (fs.existsSync(pxt.CONFIG_NAME)) {
165912
165953
  if (upload) {
165913
165954
  U.userError("upload only supported on targets");
@@ -165920,7 +165961,7 @@ async function bumpAsync(parsed) {
165920
165961
  .then(() => nodeutil.runGitAsync("pull"))
165921
165962
  .then(() => nodeutil.createBranchAsync(newBranchName))
165922
165963
  .then(() => justBumpPkgAsync(bumpType, false)) // don't tag when creating a PR
165923
- .then((version) => nodeutil.gitPushAsync().then(() => version))
165964
+ .then((version) => nodeutil.gitPushAsync(false).then(() => version))
165924
165965
  .then((version) => nodeutil.createPullRequestAsync({
165925
165966
  title: `[pxt-cli] bump version to ${version}`,
165926
165967
  body: "__Do not edit the PR title.__\n" +
@@ -165940,8 +165981,7 @@ async function bumpAsync(parsed) {
165940
165981
  return Promise.resolve()
165941
165982
  .then(() => nodeutil.runGitAsync("pull"))
165942
165983
  .then(() => justBumpPkgAsync(bumpType))
165943
- .then(() => nodeutil.runGitAsync("push", "--follow-tags"))
165944
- .then(() => nodeutil.runGitAsync("push"));
165984
+ .then(() => nodeutil.runGitAsync("push", "--atomic", "--follow-tags"));
165945
165985
  }
165946
165986
  }
165947
165987
  else if (fs.existsSync("pxtarget.json"))
@@ -165954,7 +165994,7 @@ async function bumpAsync(parsed) {
165954
165994
  .then(() => nodeutil.createBranchAsync(newBranchName))
165955
165995
  .then(() => bumpPxt ? bumpPxtCoreDepAsync() : Promise.resolve())
165956
165996
  .then(() => nodeutil.npmVersionBumpAsync(bumpType, false)) // don't tag when creating a PR
165957
- .then((version) => nodeutil.gitPushAsync().then(() => version))
165997
+ .then((version) => nodeutil.gitPushAsync(false).then(() => version))
165958
165998
  .then((version) => nodeutil.createPullRequestAsync({
165959
165999
  title: `[pxt-cli] bump version to ${version}`,
165960
166000
  body: "__Do not edit the PR title.__\n" +
@@ -165973,10 +166013,9 @@ async function bumpAsync(parsed) {
165973
166013
  console.log("Bumping via direct push.");
165974
166014
  return Promise.resolve()
165975
166015
  .then(() => nodeutil.runGitAsync("pull"))
165976
- .then(() => bumpPxt ? bumpPxtCoreDepAsync().then(() => nodeutil.runGitAsync("push")) : Promise.resolve())
166016
+ .then(() => bumpPxt ? bumpPxtCoreDepAsync().then(() => nodeutil.runGitAsync("push", "--atomic", "--no-follow-tags")) : Promise.resolve())
165977
166017
  .then(() => nodeutil.runNpmAsync("version", bumpType))
165978
- .then(() => nodeutil.runGitAsync("push", "--follow-tags"))
165979
- .then(() => nodeutil.runGitAsync("push"))
166018
+ .then(() => nodeutil.runGitAsync("push", "--atomic", "--follow-tags"))
165980
166019
  .then(() => upload ? uploadTaggedTargetAsync() : Promise.resolve());
165981
166020
  }
165982
166021
  else {
@@ -169987,7 +170026,7 @@ async function staticpkgAsync(parsed) {
169987
170026
  pxt.log(`packaging editor to ${builtPackaged}`);
169988
170027
  await rimrafAsync(builtPackaged, {});
169989
170028
  if (bump) {
169990
- await bumpAsync();
170029
+ await bumpAsync(parsed);
169991
170030
  }
169992
170031
  if (locs) {
169993
170032
  await internalGenDocsAsync(false, true);
@@ -171838,7 +171877,7 @@ ${pxt.crowdin.KEY_VARIABLE} - crowdin key
171838
171877
  update: { description: "(package only) Updates pxt-core reference to the latest release" },
171839
171878
  upload: { description: "(package only) Upload after bumping" },
171840
171879
  pr: { description: "Bump via pull request rather than direct push" },
171841
- nopr: { description: "Bump via direct push rather than pull request (overrides --pr)" },
171880
+ nopr: { description: "Force a direct bump (overrides --pr and protection detection)" },
171842
171881
  version: {
171843
171882
  description: "Which part of the version to bump, or a complete version number to assign. Defaults to 'patch'",
171844
171883
  argument: "version",
@@ -171956,6 +171995,9 @@ ${pxt.crowdin.KEY_VARIABLE} - crowdin key
171956
171995
  bump: {
171957
171996
  description: "bump version number prior to package"
171958
171997
  },
171998
+ nopr: {
171999
+ description: "With --bump, force a direct bump rather than a pull request"
172000
+ },
171959
172001
  cloudbuild: {
171960
172002
  description: "(deprecated) forces build to happen in the cloud",
171961
172003
  aliases: ["cloud", "cloud-build", "cb"]
package/built/pxtlib.js CHANGED
@@ -1510,6 +1510,8 @@ const lf = ts.pxtc.Util.lf;
1510
1510
  let parts = k.split(".");
1511
1511
  for (let i = 0; i < parts.length; ++i) {
1512
1512
  let part = parts[i];
1513
+ if (part === "__proto__" || part === "constructor" || part === "prototype")
1514
+ Util.userError("Invalid configuration key: " + k);
1513
1515
  if (i == parts.length - 1)
1514
1516
  ptr[part] = v[k];
1515
1517
  else {
@@ -7804,10 +7806,11 @@ var pxt;
7804
7806
  if (!inEnum)
7805
7807
  return;
7806
7808
  // parse the enum case, with lots of optional stuff (?)
7807
- let mm = /^\s*(\w+)\s*(=\s*(.*?))?,?\s*$/.exec(lnNC);
7809
+ const enumLine = lnNC.trim().replace(/,$/, "").trim();
7810
+ let mm = /^(\w+)(?:\s*=(.*))?$/.exec(enumLine);
7808
7811
  if (mm) {
7809
7812
  let nm = mm[1];
7810
- let v = mm[3];
7813
+ let v = mm[2] && mm[2].trim();
7811
7814
  let opt = "";
7812
7815
  if (v) {
7813
7816
  // user-supplied value
@@ -7845,23 +7848,26 @@ var pxt;
7845
7848
  }
7846
7849
  function parseArg(parsedAttrs, s) {
7847
7850
  s = s.trim();
7848
- let m = /(.*)=\s*(-?\w+)$/.exec(s);
7851
+ const equals = s.lastIndexOf("=");
7852
+ const m = equals < 0 ? null : /^\s*(-?\w+)$/.exec(s.slice(equals + 1));
7849
7853
  let defl = "";
7850
7854
  let qm = "";
7851
7855
  if (m) {
7852
- defl = m[2];
7856
+ defl = m[1];
7853
7857
  qm = "?";
7854
- s = m[1].trim();
7858
+ s = s.slice(0, equals).trim();
7855
7859
  }
7856
- m = /^(.*?)(\w+)$/.exec(s);
7857
- if (!m) {
7860
+ let nameStart = s.length;
7861
+ while (nameStart > 0 && /\w/.test(s.charAt(nameStart - 1)))
7862
+ --nameStart;
7863
+ if (nameStart == s.length || /[\r\n\u2028\u2029]/.test(s.slice(0, nameStart))) {
7858
7864
  err("invalid argument: " + s);
7859
7865
  return {
7860
7866
  name: "???",
7861
7867
  type: "int"
7862
7868
  };
7863
7869
  }
7864
- let argName = m[2];
7870
+ let argName = s.slice(nameStart);
7865
7871
  if (parsedAttrs.paramDefl[argName]) {
7866
7872
  defl = parsedAttrs.paramDefl[argName];
7867
7873
  qm = "?";
@@ -7876,7 +7882,7 @@ var pxt;
7876
7882
  }
7877
7883
  return {
7878
7884
  name: argName + qm,
7879
- type: m[1]
7885
+ type: s.slice(0, nameStart)
7880
7886
  };
7881
7887
  }
7882
7888
  function parseCpp(src, isHeader) {
@@ -7886,9 +7892,28 @@ var pxt;
7886
7892
  inDocComment = false;
7887
7893
  let indexedInstanceAttrs;
7888
7894
  let indexedInstanceIdx = -1;
7889
- // replace #if 0 .... #endif with newlines
7890
- src = src.replace(/^(\s*#\s*if\s+(\w+)\s*$)([^]*?)(^\s*#\s*(elif|else|endif)\s*$)/mg, (f, _if, arg, middle, _endif) => hash_if_options.indexOf(arg) >= 0 && !cpp_options[arg] ?
7891
- _if + middle.replace(/[^\n]/g, "") + _endif : f);
7895
+ // Match directives one line at a time so unterminated blocks cannot rescan the source.
7896
+ const lines = src.split(/\r?\n/);
7897
+ let ifStart = -1;
7898
+ let ifArg = "";
7899
+ lines.forEach((ln, i) => {
7900
+ const directive = /^#\s*(?:if\s+(\w+)|(elif|else|endif))$/.exec(ln.trim());
7901
+ if (!directive)
7902
+ return;
7903
+ if (ifStart < 0) {
7904
+ if (directive[1]) {
7905
+ ifStart = i;
7906
+ ifArg = directive[1];
7907
+ }
7908
+ }
7909
+ else if (directive[2]) {
7910
+ if (hash_if_options.indexOf(ifArg) >= 0 && !cpp_options[ifArg]) {
7911
+ for (let j = ifStart + 1; j < i; ++j)
7912
+ lines[j] = "";
7913
+ }
7914
+ ifStart = -1;
7915
+ }
7916
+ });
7892
7917
  // special handling of C++ namespace that ends with Methods (e.g. FooMethods)
7893
7918
  // such a namespace will be converted into a TypeScript interface
7894
7919
  // this enables simple objects with methods to be defined. See, for example:
@@ -7966,10 +7991,14 @@ var pxt;
7966
7991
  return "T";
7967
7992
  case "String":
7968
7993
  return "S";
7969
- default:
7994
+ default: {
7970
7995
  if (U.lookup(knownEnums, tp))
7971
7996
  return "I";
7972
- return "_" + tp.replace(/[\*_]+$/, "");
7997
+ let typeEnd = tp.length;
7998
+ while (typeEnd > 0 && /[\*_]/.test(tp.charAt(typeEnd - 1)))
7999
+ --typeEnd;
8000
+ return "_" + tp.slice(0, typeEnd);
8001
+ }
7973
8002
  }
7974
8003
  }
7975
8004
  function generateVMWrapper(fi, argTypes) {
@@ -8051,7 +8080,7 @@ var pxt;
8051
8080
  enumVal = 0;
8052
8081
  enumsDTS.setNs("");
8053
8082
  shimsDTS.setNs("");
8054
- src.split(/\r?\n/).forEach(ln => {
8083
+ lines.forEach(ln => {
8055
8084
  ++lineNo;
8056
8085
  // remove comments (NC = no comments)
8057
8086
  let lnNC = stripComments(ln);
@@ -27237,12 +27266,20 @@ ${opts.repo.name.replace(/^pxt-/, '')}=github:${opts.repo.fullName}#${opts.repo.
27237
27266
  strong: 1,
27238
27267
  em: 1,
27239
27268
  };
27269
+ /** Keeps metadata and token-only HTML out of the translation catalog. */
27270
+ function isNonTranslatable(text) {
27271
+ if (text.startsWith("IE=edge,") || text.startsWith("width=device-width"))
27272
+ return true;
27273
+ // Remove URLs, template placeholders, and spacing entities to look for prose.
27274
+ const prose = text.replace(/(?:https?:\/\/|\/)[\w@\/.]+|@[-\w]+@|\{[^{}]+\}|&nbsp;/g, "");
27275
+ return !/[a-zA-Z]/.test(prose);
27276
+ }
27240
27277
  function translate(html, locale) {
27241
27278
  const missing = {};
27242
27279
  function translateOne(toTranslate) {
27243
27280
  let spm = /^(\s*)([^]*?)(\s*)$/.exec(toTranslate);
27244
27281
  let text = spm[2].replace(/\s+/g, " ");
27245
- if (text == "" || /^((IE=edge,.*|width=device-width.*|(https?:\/\/|\/)[\w@\/\.]+|@[\-\w]+@|\{[^\{\}]+\}|[^a-zA-Z]*|(&nbsp;)+)\s*)+$/.test(text))
27282
+ if (isNonTranslatable(text))
27246
27283
  return null;
27247
27284
  let v = U.lookup(locale, text);
27248
27285
  if (v)