pxt-core 8.1.2 → 8.1.5

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
@@ -103374,7 +103374,9 @@ var pxt;
103374
103374
  const height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
103375
103375
  const left = ((width / 2) - (popUpWidth / 2)) + winLeft;
103376
103376
  const top = ((height / 2) - (popUpHeight / 2)) + winTop;
103377
- const popupWindow = window.open(url, title, "width=" + popUpWidth + ", height=" + popUpHeight + ", top=" + top + ", left=" + left);
103377
+ const features = "width=" + popUpWidth + ", height=" + popUpHeight + ", top=" + top + ", left=" + left;
103378
+ // Current CEF version does not like when features parameter is passed and just immediately rejects.
103379
+ const popupWindow = window.open(url, title, !pxt.BrowserUtils.isIpcRenderer() ? features : undefined);
103378
103380
  if (popupWindow.focus) {
103379
103381
  popupWindow.focus();
103380
103382
  }
@@ -103382,7 +103384,7 @@ var pxt;
103382
103384
  }
103383
103385
  catch (e) {
103384
103386
  // Error opening popup
103385
- pxt.tickEvent('pxt.popupError', { url: url, msg: e.message });
103387
+ pxt.tickEvent('pxt.popupError', { msg: e.message });
103386
103388
  return null;
103387
103389
  }
103388
103390
  }
@@ -106384,11 +106386,16 @@ var pxt;
106384
106386
  docs.prepTemplate = prepTemplate;
106385
106387
  function setupRenderer(renderer) {
106386
106388
  renderer.image = function (href, title, text) {
106389
+ const endpointName = "makecode-lucas-testing-makecodetempmediaservice-usea";
106387
106390
  if (href.startsWith("youtube:")) {
106388
106391
  let out = '<div class="tutorial-video-embed"><iframe src="https://www.youtube.com/embed/' + href.split(":").pop()
106389
106392
  + '" title="' + title + '" frameborder="0" ' + 'allowFullScreen ' + 'allow="autoplay; picture-in-picture"></iframe></div>';
106390
106393
  return out;
106391
106394
  }
106395
+ else if (href.startsWith("azuremedia:")) {
106396
+ let out = `<div class="tutorial-video-embed"><video class="ams-embed" controls src="https://${endpointName}.streaming.media.azure.net/` + href.split(":").pop() + '/manifest(format=mpd-time-cmaf)" /></div>';
106397
+ return out;
106398
+ }
106392
106399
  else {
106393
106400
  let out = '<img class="ui image" src="' + href + '" alt="' + text + '"';
106394
106401
  if (title) {
@@ -125394,6 +125401,56 @@ ${output}</xml>`;
125394
125401
  }
125395
125402
  return undefined;
125396
125403
  }
125404
+ function getCallKey(statement) {
125405
+ const call = statement.expression;
125406
+ const callInfo = pxtc.pxtInfo(call).callInfo;
125407
+ const attributes = attrs(callInfo);
125408
+ if (attributes.blockAllowMultiple)
125409
+ return undefined;
125410
+ if (attributes.blockHandlerKey)
125411
+ return attributes.blockHandlerKey;
125412
+ const args = callInfo.args.filter(arg => !ts.isArrowFunction(arg) && !isFunctionExpression(arg)).map(arg => getArgKeyRecursive(arg)).join("$$");
125413
+ return attributes.blockId + "-" + args;
125414
+ }
125415
+ // custom function to get the key for an argument. we do this instead of simply calling getText() because this
125416
+ // handles whitespace and certain passthrough nodes like parenthesized expressions
125417
+ function getArgKeyRecursive(n) {
125418
+ if (!n)
125419
+ return "";
125420
+ switch (n.kind) {
125421
+ case SK.ParenthesizedExpression:
125422
+ case SK.AsExpression:
125423
+ return getArgKeyRecursive(n.expression);
125424
+ case SK.Identifier:
125425
+ return n.text;
125426
+ case SK.StringLiteral:
125427
+ case SK.FirstTemplateToken:
125428
+ case SK.NoSubstitutionTemplateLiteral:
125429
+ return `"${n.text}"`;
125430
+ case SK.NumericLiteral:
125431
+ return n.text;
125432
+ case SK.TrueKeyword:
125433
+ return "true";
125434
+ case SK.FalseKeyword:
125435
+ return "false";
125436
+ case SK.BinaryExpression:
125437
+ return `{${getArgKeyRecursive(n.left)}${n.operatorToken.getText()}${getArgKeyRecursive(n.right)}}`;
125438
+ case SK.PrefixUnaryExpression:
125439
+ return n.operator + getArgKeyRecursive(n.operand);
125440
+ case SK.PropertyAccessExpression:
125441
+ return getArgKeyRecursive(n.expression) + "." + getArgKeyRecursive(n.name);
125442
+ case SK.ArrayLiteralExpression:
125443
+ return `[${n.elements.map(e => getArgKeyRecursive(e))}]`;
125444
+ case SK.ElementAccessExpression:
125445
+ return `${n.expression}[${n.argumentExpression}]`;
125446
+ case SK.TaggedTemplateExpression:
125447
+ return `${getArgKeyRecursive(n.tag)}\`${n.template.getText()}\``;
125448
+ case SK.CallExpression:
125449
+ return `${getArgKeyRecursive(n.expression)}(${n.arguments.map(getArgKeyRecursive).join(",")})`;
125450
+ default:
125451
+ return n.getText();
125452
+ }
125453
+ }
125397
125454
  function countBlock() {
125398
125455
  emittedBlocks++;
125399
125456
  if (emittedBlocks > MAX_BLOCKS) {
@@ -125963,6 +126020,101 @@ ${output}</xml>`;
125963
126020
  }
125964
126021
  return undefined;
125965
126022
  }
126023
+ /**
126024
+ * We split up comments according to the following rules:
126025
+ * 1. If the comment is not top-level:
126026
+ * a. Combine it with all comments for the following statement
126027
+ * b. If there is no following statement in the current block, group it with the previous statement
126028
+ * c. If there are no statements inside the block, group it with the parent block
126029
+ * d. If trailing the same line as the statement, group it with the comments for that statement
126030
+ * 2. If the comment is top-level:
126031
+ * b. If the comment is followed by an empty line, it becomes a workspace comment
126032
+ * c. If the comment is followed by a multi-line comment, it becomes a workspace comment
126033
+ * a. If the comment is a single-line comment, combine it with the next single-line comment
126034
+ * d. If the comment is not followed with an empty line, group it with the next statement or event
126035
+ * e. All other comments are workspace comments
126036
+ *
126037
+ * The below function gathers any comments associated to the Node, and attaches them
126038
+ * to the StatementNode.
126039
+ */
126040
+ function getNodeComments(commented, stmt) {
126041
+ let comments = [];
126042
+ let current;
126043
+ for (let i = 0; i < commentMap.length; i++) {
126044
+ current = commentMap[i];
126045
+ if (!current.owner && current.start >= commented.pos && current.end <= commented.end) {
126046
+ current.owner = commented;
126047
+ current.ownerStatement = stmt;
126048
+ comments.push(current);
126049
+ }
126050
+ if (current.start > commented.end)
126051
+ break;
126052
+ }
126053
+ if (current && current.isTrailingComment) {
126054
+ const endLine = ts.getLineAndCharacterOfPosition(file, commented.end);
126055
+ const commentLine = ts.getLineAndCharacterOfPosition(file, current.start);
126056
+ if (endLine.line === commentLine.line) {
126057
+ // If the comment is trailing and on the same line as the statement, it probably belongs
126058
+ // to this statement. Remove it from any statement it's already assigned to and any workspace
126059
+ // comments
126060
+ if (current.ownerStatement) {
126061
+ current.ownerStatement.comment.splice(current.ownerStatement.comment.indexOf(current), 1);
126062
+ for (const wsComment of workspaceComments) {
126063
+ wsComment.comment.splice(wsComment.comment.indexOf(current), 1);
126064
+ }
126065
+ }
126066
+ current.owner = commented;
126067
+ current.ownerStatement = stmt;
126068
+ comments.push(current);
126069
+ }
126070
+ }
126071
+ if (comments.length) {
126072
+ const wsCommentRefs = [];
126073
+ if (isTopLevelComment(commented)) {
126074
+ let currentWorkspaceComment = [];
126075
+ const localWorkspaceComments = [];
126076
+ comments.forEach((comment, index) => {
126077
+ let beforeStatement = comment.owner && comment.start < comment.owner.getStart();
126078
+ if (comment.kind === CommentKind.MultiLine && beforeStatement) {
126079
+ if (currentWorkspaceComment.length) {
126080
+ localWorkspaceComments.push(currentWorkspaceComment);
126081
+ currentWorkspaceComment = [];
126082
+ }
126083
+ if (index != comments.length - 1) {
126084
+ localWorkspaceComments.push([comment]);
126085
+ return;
126086
+ }
126087
+ }
126088
+ currentWorkspaceComment.push(comment);
126089
+ if (comment.followedByEmptyLine && beforeStatement) {
126090
+ localWorkspaceComments.push(currentWorkspaceComment);
126091
+ currentWorkspaceComment = [];
126092
+ }
126093
+ });
126094
+ comments = currentWorkspaceComment;
126095
+ localWorkspaceComments.forEach(comment => {
126096
+ const refId = getCommentRef();
126097
+ wsCommentRefs.push(refId);
126098
+ workspaceComments.push({ comment, refId });
126099
+ });
126100
+ }
126101
+ // Attach comments to StatementNode
126102
+ if (stmt) {
126103
+ if (wsCommentRefs.length) {
126104
+ if (stmt.data)
126105
+ stmt.data += ";" + wsCommentRefs.join(";");
126106
+ else
126107
+ stmt.data = wsCommentRefs.join(";");
126108
+ }
126109
+ if (comments && comments.length) {
126110
+ if (stmt.comment)
126111
+ stmt.comment = stmt.comment.concat(comments);
126112
+ else
126113
+ stmt.comment = comments;
126114
+ }
126115
+ }
126116
+ }
126117
+ }
125966
126118
  function getStatementBlock(n, next, parent, asExpression = false, topLevel = false) {
125967
126119
  const node = n;
125968
126120
  let stmt;
@@ -126065,7 +126217,7 @@ ${output}</xml>`;
126065
126217
  }
126066
126218
  }
126067
126219
  if (!skipComments) {
126068
- getComments(parent || node);
126220
+ getNodeComments(parent || node, stmt);
126069
126221
  }
126070
126222
  return stmt;
126071
126223
  function getNext() {
@@ -126074,97 +126226,6 @@ ${output}</xml>`;
126074
126226
  }
126075
126227
  return undefined;
126076
126228
  }
126077
- /**
126078
- * We split up comments according to the following rules:
126079
- * 1. If the comment is not top-level:
126080
- * a. Combine it with all comments for the following statement
126081
- * b. If there is no following statement in the current block, group it with the previous statement
126082
- * c. If there are no statements inside the block, group it with the parent block
126083
- * d. If trailing the same line as the statement, group it with the comments for that statement
126084
- * 2. If the comment is top-level:
126085
- * b. If the comment is followed by an empty line, it becomes a workspace comment
126086
- * c. If the comment is followed by a multi-line comment, it becomes a workspace comment
126087
- * a. If the comment is a single-line comment, combine it with the next single-line comment
126088
- * d. If the comment is not followed with an empty line, group it with the next statement or event
126089
- * e. All other comments are workspace comments
126090
- */
126091
- function getComments(commented) {
126092
- let comments = [];
126093
- let current;
126094
- for (let i = 0; i < commentMap.length; i++) {
126095
- current = commentMap[i];
126096
- if (!current.owner && current.start >= commented.pos && current.end <= commented.end) {
126097
- current.owner = commented;
126098
- current.ownerStatement = stmt;
126099
- comments.push(current);
126100
- }
126101
- if (current.start > commented.end)
126102
- break;
126103
- }
126104
- if (current && current.isTrailingComment) {
126105
- const endLine = ts.getLineAndCharacterOfPosition(file, commented.end);
126106
- const commentLine = ts.getLineAndCharacterOfPosition(file, current.start);
126107
- if (endLine.line === commentLine.line) {
126108
- // If the comment is trailing and on the same line as the statement, it probably belongs
126109
- // to this statement. Remove it from any statement it's already assigned to and any workspace
126110
- // comments
126111
- if (current.ownerStatement) {
126112
- current.ownerStatement.comment.splice(current.ownerStatement.comment.indexOf(current), 1);
126113
- for (const wsComment of workspaceComments) {
126114
- wsComment.comment.splice(wsComment.comment.indexOf(current), 1);
126115
- }
126116
- }
126117
- current.owner = commented;
126118
- current.ownerStatement = stmt;
126119
- comments.push(current);
126120
- }
126121
- }
126122
- if (comments.length) {
126123
- const wsCommentRefs = [];
126124
- if (isTopLevelComment(commented)) {
126125
- let currentWorkspaceComment = [];
126126
- const localWorkspaceComments = [];
126127
- comments.forEach((comment, index) => {
126128
- let beforeStatement = comment.owner && comment.start < comment.owner.getStart();
126129
- if (comment.kind === CommentKind.MultiLine && beforeStatement) {
126130
- if (currentWorkspaceComment.length) {
126131
- localWorkspaceComments.push(currentWorkspaceComment);
126132
- currentWorkspaceComment = [];
126133
- }
126134
- if (index != comments.length - 1) {
126135
- localWorkspaceComments.push([comment]);
126136
- return;
126137
- }
126138
- }
126139
- currentWorkspaceComment.push(comment);
126140
- if (comment.followedByEmptyLine && beforeStatement) {
126141
- localWorkspaceComments.push(currentWorkspaceComment);
126142
- currentWorkspaceComment = [];
126143
- }
126144
- });
126145
- comments = currentWorkspaceComment;
126146
- localWorkspaceComments.forEach(comment => {
126147
- const refId = getCommentRef();
126148
- wsCommentRefs.push(refId);
126149
- workspaceComments.push({ comment, refId });
126150
- });
126151
- }
126152
- if (stmt) {
126153
- if (wsCommentRefs.length) {
126154
- if (stmt.data)
126155
- stmt.data += ";" + wsCommentRefs.join(";");
126156
- else
126157
- stmt.data = wsCommentRefs.join(";");
126158
- }
126159
- if (comments && comments.length) {
126160
- if (stmt.comment)
126161
- stmt.comment = stmt.comment.concat(comments);
126162
- else
126163
- stmt.comment = comments;
126164
- }
126165
- }
126166
- }
126167
- }
126168
126229
  }
126169
126230
  function getTypeScriptStatementBlock(node, prefix, err) {
126170
126231
  if (options.errorOnGreyBlocks)
@@ -126931,15 +126992,29 @@ ${output}</xml>`;
126931
126992
  blockStatements.unshift(statement);
126932
126993
  }
126933
126994
  }
126934
- eventStatements.map(n => getStatementBlock(n, undefined, undefined, false, topLevel)).forEach(emitStatementNode);
126995
+ const compiledEvents = [];
126996
+ const keyMap = {};
126997
+ for (const statement of eventStatements) {
126998
+ const key = statement.kind === SK.FunctionDeclaration ? undefined : getCallKey(statement);
126999
+ if (key && keyMap[key]) {
127000
+ // Pass false for topLevel to force a grey block
127001
+ compiledEvents.push(getStatementBlock(statement, undefined, undefined, false, false));
127002
+ }
127003
+ else {
127004
+ keyMap[key] = true;
127005
+ compiledEvents.push(getStatementBlock(statement, undefined, undefined, false, topLevel));
127006
+ }
127007
+ }
127008
+ compiledEvents.forEach(emitStatementNode);
126935
127009
  if (blockStatements.length) {
126936
127010
  // wrap statement in "on start" if top level
126937
- const stmtNode = blockStatements.shift();
126938
- const stmt = getStatementBlock(stmtNode, blockStatements, parent, false, topLevel);
127011
+ const blockNode = blockStatements.shift();
127012
+ const stmt = getStatementBlock(blockNode, blockStatements, parent, false, topLevel);
126939
127013
  if (emitOnStart) {
126940
127014
  // Preserve any variable edeclarations that were never used
126941
- let current = stmt;
126942
- let currentNode = stmtNode;
127015
+ // stmt and blockNode will not yet align if there was an auto-declared variable.
127016
+ let currentStmtNode = stmt;
127017
+ let currentBlockNode = blockNode;
126943
127018
  autoDeclarations.forEach(([name, node]) => {
126944
127019
  if (varUsages[name] === ReferenceType.InBlocksOnly) {
126945
127020
  return;
@@ -126953,17 +127028,21 @@ ${output}</xml>`;
126953
127028
  v = getTypeScriptStatementBlock(node, "let ");
126954
127029
  }
126955
127030
  else {
126956
- v = getVariableSetOrChangeBlock(stmtNode, node.name, node.initializer, false, true);
127031
+ v = getVariableSetOrChangeBlock(blockNode, node.name, node.initializer, false, true);
126957
127032
  }
126958
- v.next = current;
126959
- current = v;
126960
- currentNode = node;
127033
+ // Auto-declared variables were not previously inserted into the stmt linked-list.
127034
+ // Insert auto-declared 'v' as the currentStmtNode.
127035
+ v.next = currentStmtNode;
127036
+ currentStmtNode = v;
127037
+ currentBlockNode = node;
127038
+ // Attach to currentStmtNode any associated comments
127039
+ getNodeComments(currentBlockNode.parent, currentStmtNode);
126961
127040
  });
126962
- if (current) {
126963
- const r = mkStmt(ts.pxtc.ON_START_TYPE, currentNode);
127041
+ if (currentStmtNode) {
127042
+ const r = mkStmt(ts.pxtc.ON_START_TYPE, currentBlockNode);
126964
127043
  r.handlers = [{
126965
127044
  name: "HANDLER",
126966
- statement: current
127045
+ statement: currentStmtNode
126967
127046
  }];
126968
127047
  return r;
126969
127048
  }
@@ -153180,6 +153259,7 @@ var pxsim;
153180
153259
  }
153181
153260
  }
153182
153261
  createFrame(url) {
153262
+ var _a;
153183
153263
  const wrapper = document.createElement("div");
153184
153264
  wrapper.className = `simframe ui embed`;
153185
153265
  const frame = document.createElement('iframe');
@@ -153194,7 +153274,7 @@ var pxsim;
153194
153274
  frame.frameBorder = "0";
153195
153275
  frame.dataset['runid'] = this.runId;
153196
153276
  frame.dataset['origin'] = new URL(furl).origin || "*";
153197
- if (this._runOptions.autofocus)
153277
+ if ((_a = this._runOptions) === null || _a === void 0 ? void 0 : _a.autofocus)
153198
153278
  frame.setAttribute("autofocus", "true");
153199
153279
  wrapper.appendChild(frame);
153200
153280
  const i = document.createElement("i");
@@ -157722,11 +157802,11 @@ function onlyExts(files, exts) {
157722
157802
  }
157723
157803
  function pxtFileList(pref) {
157724
157804
  return nodeutil.allFiles(pref + "webapp/public")
157725
- .concat(onlyExts(nodeutil.allFiles(pref + "built/web", 1), [".js", ".css"]))
157726
- .concat(nodeutil.allFiles(pref + "built/web/fonts", 1))
157727
- .concat(nodeutil.allFiles(pref + "built/web/vs", 4))
157728
- .concat(nodeutil.allFiles(pref + "built/web/skillmap", 4))
157729
- .concat(nodeutil.allFiles(pref + "built/web/authcode", 4));
157805
+ .concat(onlyExts(nodeutil.allFiles(pref + "built/web", { maxDepth: 1 }), [".js", ".css"]))
157806
+ .concat(nodeutil.allFiles(pref + "built/web/fonts", { maxDepth: 1 }))
157807
+ .concat(nodeutil.allFiles(pref + "built/web/vs", { maxDepth: 4 }))
157808
+ .concat(nodeutil.allFiles(pref + "built/web/skillmap", { maxDepth: 4 }))
157809
+ .concat(nodeutil.allFiles(pref + "built/web/authcode", { maxDepth: 4 }));
157730
157810
  }
157731
157811
  function semverCmp(a, b) {
157732
157812
  let parse = (s) => {
@@ -158038,7 +158118,7 @@ function targetFileList() {
158038
158118
  let lst = onlyExts(nodeutil.allFiles("built"), [".js", ".css", ".json", ".webmanifest"])
158039
158119
  .concat(nodeutil.allFiles(path.join(simDir(), "public")));
158040
158120
  if (simDir() != "sim")
158041
- lst = lst.concat(nodeutil.allFiles(path.join("sim", "public"), 5, true));
158121
+ lst = lst.concat(nodeutil.allFiles(path.join("sim", "public"), { maxDepth: 5, allowMissing: true }));
158042
158122
  pxt.debug(`target files (on disk): ${lst.join('\r\n ')}`);
158043
158123
  return lst;
158044
158124
  }
@@ -158553,7 +158633,7 @@ function forEachBundledPkgAsync(f, includeProjects = false) {
158553
158633
  let prev = process.cwd();
158554
158634
  let folders = pxt.appTarget.bundleddirs;
158555
158635
  if (includeProjects) {
158556
- let projects = nodeutil.allFiles("libs", 1, /*allowMissing*/ false, /*includeDirs*/ true).filter(f => /prj$/.test(f));
158636
+ let projects = nodeutil.allFiles("libs", { maxDepth: 1, includeDirs: true }).filter(f => /prj$/.test(f));
158557
158637
  folders = folders.concat(projects);
158558
158638
  }
158559
158639
  return U.promiseMapAllSeries(folders, (dirname) => {
@@ -158718,7 +158798,7 @@ function buildEditorExtensionAsync(dirname, optionName) {
158718
158798
  else
158719
158799
  p = buildFolderAsync(dirname, true, dirname);
158720
158800
  return p.then(() => {
158721
- const prepends = nodeutil.allFiles(path.join(dirname, "prepend"), 1, true)
158801
+ const prepends = nodeutil.allFiles(path.join(dirname, "prepend"), { maxDepth: 1, allowMissing: true })
158722
158802
  .filter(f => /\.js$/.test(f));
158723
158803
  if (prepends && prepends.length) {
158724
158804
  const editorjs = path.join("built", dirname + ".js");
@@ -159319,7 +159399,7 @@ function updateDefaultProjects(cfg) {
159319
159399
  pxt.BLOCKS_PROJECT_NAME,
159320
159400
  pxt.JAVASCRIPT_PROJECT_NAME
159321
159401
  ];
159322
- nodeutil.allFiles("libs", 1, /*allowMissing*/ false, /*includeDirs*/ true)
159402
+ nodeutil.allFiles("libs", { maxDepth: 1, includeDirs: true })
159323
159403
  .filter((f) => {
159324
159404
  return defaultProjects.indexOf(path.basename(f)) !== -1;
159325
159405
  })
@@ -159780,7 +159860,7 @@ function renderDocs(builtPackaged, localDir) {
159780
159860
  docFolders.push(...nodeutil.getBundledPackagesDocs());
159781
159861
  docFolders.push("docs");
159782
159862
  for (const docFolder of docFolders) {
159783
- for (const f of nodeutil.allFiles(docFolder, 8)) {
159863
+ for (const f of nodeutil.allFiles(docFolder, { maxDepth: 8 })) {
159784
159864
  pxt.log(`rendering ${f}`);
159785
159865
  const pathUnderDocs = f.slice(docFolder.length + 1);
159786
159866
  let outputFile = path.join(dst, "docs", pathUnderDocs);
@@ -161904,7 +161984,7 @@ function buildJResSpritesDirectoryAsync(dir) {
161904
161984
  return [(v >> 16) & 0xff, (v >> 8) & 0xff, (v >> 0) & 0xff];
161905
161985
  });
161906
161986
  let ts = `namespace ${metaInfo.star.namespace} {\n`;
161907
- for (let fn of nodeutil.allFiles(dir, 1)) {
161987
+ for (let fn of nodeutil.allFiles(dir, { maxDepth: 1 })) {
161908
161988
  fn = fn.replace(/\\/g, "/");
161909
161989
  let m = /(.*\/)(.*)\.png$/i.exec(fn);
161910
161990
  if (!m)
@@ -162479,6 +162559,12 @@ function internalCheckDocsAsync(compileSnippets, re, fix, pycheck) {
162479
162559
  const docsRoot = nodeutil.targetDir;
162480
162560
  const docsTemplate = server.expandDocFileTemplate("docs.html");
162481
162561
  pxt.log(`checking docs`);
162562
+ const ignoredFoldersKey = ".checkdocs-ignore";
162563
+ const ignoredFolders = nodeutil.allFiles("docs", { includeHiddenFiles: true })
162564
+ .filter(el => el.endsWith(ignoredFoldersKey))
162565
+ .map(el => path.dirname(path.join(el.substring(4))) + path.sep);
162566
+ if (ignoredFolders.length)
162567
+ pxt.log(`Ignoring folders [${ignoredFolders.join(", ")}]`);
162482
162568
  const noTOCs = [];
162483
162569
  const todo = [];
162484
162570
  let urls = {};
@@ -162487,12 +162573,12 @@ function internalCheckDocsAsync(compileSnippets, re, fix, pycheck) {
162487
162573
  // only check each snippet once.
162488
162574
  const existingSnippets = {};
162489
162575
  let snippets = [];
162490
- const maxFileSize = checkFileSize(nodeutil.allFiles("docs", 10, true, true, ".ignorelargefiles"));
162576
+ const maxFileSize = checkFileSize(nodeutil.allFiles("docs", { maxDepth: 10, allowMissing: true, includeDirs: true, ignoredFileMarker: ".ignorelargefiles" }));
162491
162577
  if (!pxt.appTarget.ignoreDocsErrors
162492
162578
  && maxFileSize > (pxt.appTarget.cloud.maxFileSize || (5000000)))
162493
162579
  U.userError(`files too big in docs folder`);
162494
162580
  // scan and fix image links
162495
- nodeutil.allFiles("docs")
162581
+ nodeutil.allFiles("docs", { ignoredFileMarker: ignoredFoldersKey })
162496
162582
  .filter(f => /\.md/.test(f))
162497
162583
  .forEach(f => {
162498
162584
  let md = fs.readFileSync(f, { encoding: "utf8" });
@@ -162547,6 +162633,9 @@ function internalCheckDocsAsync(compileSnippets, re, fix, pycheck) {
162547
162633
  urls[url] = isResource
162548
162634
  ? nodeutil.fileExistsSync(path.join(docsRoot, "docs", url))
162549
162635
  : nodeutil.resolveMd(docsRoot, url);
162636
+ const pathedUrl = path.join(url);
162637
+ if (ignoredFolders.some(el => pathedUrl.startsWith(el)))
162638
+ return;
162550
162639
  if (!isResource && urls[url])
162551
162640
  todo.push(url);
162552
162641
  }
@@ -162564,7 +162653,8 @@ function internalCheckDocsAsync(compileSnippets, re, fix, pycheck) {
162564
162653
  entry.subitems.forEach(checkTOCEntry);
162565
162654
  }
162566
162655
  // check over TOCs
162567
- nodeutil.allFiles("docs", 5).filter(f => /SUMMARY\.md$/.test(f))
162656
+ nodeutil.allFiles("docs", { maxDepth: 5, ignoredFileMarker: ignoredFoldersKey })
162657
+ .filter(f => /SUMMARY\.md$/.test(f))
162568
162658
  .forEach(summaryFile => {
162569
162659
  const summaryPath = path.join(path.dirname(summaryFile), 'SUMMARY').replace(/^docs[\/\\]/, '');
162570
162660
  pxt.log(`looking for ${summaryPath}`);
@@ -162602,7 +162692,8 @@ function internalCheckDocsAsync(compileSnippets, re, fix, pycheck) {
162602
162692
  if (targetDirs) {
162603
162693
  targetDirs.forEach(dir => {
162604
162694
  pxt.log(`looking for markdown files in ${dir}`);
162605
- nodeutil.allFiles(path.join("docs", dir), 3).filter(f => mdRegex.test(f))
162695
+ nodeutil.allFiles(path.join("docs", dir), { maxDepth: 3, ignoredFileMarker: ".checkdocs-ignore" })
162696
+ .filter(f => mdRegex.test(f))
162606
162697
  .forEach(md => {
162607
162698
  pushUrl(md.slice(5).replace(mdRegex, ""), true);
162608
162699
  });
@@ -162679,6 +162770,9 @@ function internalCheckDocsAsync(compileSnippets, re, fix, pycheck) {
162679
162770
  card.otherActions.forEach(a => { if (a.url)
162680
162771
  urls.push(a.url); });
162681
162772
  for (let url of urls) {
162773
+ const pathedUrl = path.join(url);
162774
+ if (ignoredFolders.some(el => pathedUrl.startsWith(el)))
162775
+ return;
162682
162776
  const tutorialMd = nodeutil.resolveMd(docsRoot, url);
162683
162777
  if (!tutorialMd) {
162684
162778
  pxt.log(`unable to resolve ${url}`);
@@ -162728,6 +162822,9 @@ function internalCheckDocsAsync(compileSnippets, re, fix, pycheck) {
162728
162822
  card.otherActions.forEach(a => { if (a.url)
162729
162823
  urls.push(a.url); });
162730
162824
  for (let url of urls) {
162825
+ const pathedUrl = path.join(url);
162826
+ if (ignoredFolders.some(el => pathedUrl.startsWith(el)))
162827
+ return;
162731
162828
  const exMd = nodeutil.resolveMd(docsRoot, url);
162732
162829
  if (!exMd) {
162733
162830
  pxt.log(`unable to resolve ${url}`);
@@ -162777,7 +162874,7 @@ function internalCheckDocsAsync(compileSnippets, re, fix, pycheck) {
162777
162874
  async function upgradeCardsAsync() {
162778
162875
  const docsRoot = nodeutil.targetDir;
162779
162876
  // markdowns with cards
162780
- const mds = nodeutil.allFiles(docsRoot, 10, false, false)
162877
+ const mds = nodeutil.allFiles(docsRoot, { maxDepth: 10 })
162781
162878
  .filter(fn => /\.md$/.test(fn))
162782
162879
  .map(fn => ({ filename: fn, content: nodeutil.readText(fn) }))
162783
162880
  .filter(f => /```codecard/.test(f.content));
@@ -162805,7 +162902,7 @@ function internalCacheUsedBlocksAsync() {
162805
162902
  if (targetDirs) {
162806
162903
  targetDirs.forEach(dir => {
162807
162904
  pxt.log(`looking for tutorial markdown in ${dir}`);
162808
- nodeutil.allFiles(path.join("docs", dir), 3).filter(f => mdRegex.test(f))
162905
+ nodeutil.allFiles(path.join("docs", dir), { maxDepth: 3 }).filter(f => mdRegex.test(f))
162809
162906
  .forEach(md => {
162810
162907
  mdPaths.push(md.slice(5).replace(mdRegex, ""));
162811
162908
  });
@@ -162984,7 +163081,7 @@ function webstringsJson() {
162984
163081
  }
162985
163082
  function extractLocStringsAsync(output, dirs) {
162986
163083
  let prereqs = [];
162987
- dirs.forEach(dir => prereqs = prereqs.concat(nodeutil.allFiles(dir, 20)));
163084
+ dirs.forEach(dir => prereqs = prereqs.concat(nodeutil.allFiles(dir, { maxDepth: 20 })));
162988
163085
  let errCnt = 0;
162989
163086
  let translationStrings = {};
162990
163087
  function processLf(filename) {