@markw65/monkeyc-optimizer 1.0.22 → 1.0.23

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/README.md CHANGED
@@ -263,4 +263,15 @@ More fixes found via open source projects.
263
263
  - Recognize the the variable in a catch clause is a declaration
264
264
 
265
265
  - Breaking change
266
- - By popular demand, reversed the sense of inline_foo, so now it inlines when foo is _not_ declared as an excludeAnnotation
266
+ - By popular demand, reversed the sense of `inline*foo`, so now it inlines when foo is _not_ declared as an excludeAnnotation
267
+
268
+ ### 1.0.23
269
+
270
+ - Bug Fixes
271
+
272
+ - Don't treat parameters to Method types as undeclared variables
273
+ - eg `var x as (Method(a as Number, b as Number) as Void)` should not report that `a` and `b` are undeclared
274
+
275
+ - Tests
276
+ - Various new tests for module/class/local resolution of symbols
277
+ - Make tests fail by default if the optimizer reports any undefined symbols, and add `@expects` or `checkInvalidSymbols=WARNING` as needed to prevent test failures.
package/build/api.cjs CHANGED
@@ -59,7 +59,7 @@ __webpack_require__.d(__webpack_exports__, {
59
59
  "hasProperty": () => (/* binding */ api_hasProperty),
60
60
  "isStateNode": () => (/* binding */ api_isStateNode),
61
61
  "sameLookupResult": () => (/* binding */ api_sameLookupResult),
62
- "traverseAst": () => (/* binding */ api_traverseAst),
62
+ "traverseAst": () => (/* reexport */ ast_traverseAst),
63
63
  "variableDeclarationName": () => (/* binding */ api_variableDeclarationName),
64
64
  "visitReferences": () => (/* reexport */ visitor_visitReferences)
65
65
  });
@@ -71,10 +71,148 @@ var prettier_plugin_monkeyc_default = /*#__PURE__*/__webpack_require__.n(prettie
71
71
  const promises_namespaceObject = require("fs/promises");
72
72
  ;// CONCATENATED MODULE: external "prettier"
73
73
  const external_prettier_namespaceObject = require("prettier");
74
+ ;// CONCATENATED MODULE: ./src/ast.ts
75
+ /*
76
+ * This ensures that mctreeTypeInfo has every key of MCTreeTypeInfo,
77
+ * and that the corresponding arrays contain every element of the
78
+ * corresponding type.
79
+ *
80
+ * ie, any time mctree.Node changes, we'll get errors here if
81
+ * mctreeTypeInfo needs updating.
82
+ *
83
+ */
84
+ function _check(x) {
85
+ const y = x;
86
+ x = y;
87
+ }
88
+ const mctreeTypeInfo = {
89
+ ArrayExpression: ["elements"],
90
+ AssignmentExpression: ["left", "right"],
91
+ AttributeList: ["attributes"],
92
+ Attributes: ["elements"],
93
+ BinaryExpression: ["left", "right"],
94
+ Block: [],
95
+ BlockStatement: ["body", "innerComments"],
96
+ BreakStatement: [],
97
+ CallExpression: ["callee", "arguments"],
98
+ CatchClause: ["param", "body"],
99
+ CatchClauses: ["catches"],
100
+ ClassBody: ["body"],
101
+ ClassDeclaration: ["attrs", "id", "superClass", "body"],
102
+ ClassElement: ["item"],
103
+ ConditionalExpression: ["test", "consequent", "alternate"],
104
+ ContinueStatement: [],
105
+ DoWhileStatement: ["body", "test"],
106
+ EnumDeclaration: ["attrs", "id", "body"],
107
+ EnumStringBody: ["members"],
108
+ EnumStringMember: ["id", "init"],
109
+ ExpressionStatement: ["expression"],
110
+ ForStatement: ["init", "test", "body", "update"],
111
+ FunctionDeclaration: ["attrs", "id", "params", "body"],
112
+ Identifier: [],
113
+ IfStatement: ["test", "consequent", "alternate"],
114
+ ImportModule: ["id"],
115
+ InstanceOfCase: ["id"],
116
+ Line: [],
117
+ Literal: [],
118
+ LogicalExpression: ["left", "right"],
119
+ MemberExpression: ["object", "property"],
120
+ MethodDefinition: ["params", "returnType"],
121
+ ModuleDeclaration: ["attrs", "id", "body"],
122
+ MultiLine: [],
123
+ NewExpression: ["callee", "arguments"],
124
+ ObjectExpression: ["properties"],
125
+ ParenthesizedExpression: ["expression"],
126
+ Program: ["body", "comments"],
127
+ Property: ["key", "value"],
128
+ ReturnStatement: ["argument"],
129
+ SequenceExpression: ["expressions"],
130
+ SizedArrayExpression: ["size", "ts"],
131
+ SwitchCase: ["test", "consequent"],
132
+ SwitchStatement: ["discriminant", "cases"],
133
+ ThisExpression: [],
134
+ ThrowStatement: ["argument"],
135
+ TryStatement: ["block", "handler", "finalizer"],
136
+ TypedefDeclaration: ["attrs", "id", "ts"],
137
+ TypeSpecList: ["ts"],
138
+ TypeSpecPart: ["body", "callspec", "generics"],
139
+ UnaryExpression: ["argument"],
140
+ UpdateExpression: ["argument"],
141
+ Using: ["id", "as"],
142
+ VariableDeclaration: ["attrs", "declarations"],
143
+ VariableDeclarator: ["id", "init"],
144
+ WhileStatement: ["test", "body"],
145
+ };
146
+ function isMCTreeNode(node) {
147
+ return node ? typeof node === "object" && "type" in node : false;
148
+ }
149
+ /*
150
+ * Traverse the ast rooted at node, calling pre before
151
+ * visiting each node, and post after.
152
+ *
153
+ * - if pre returns false, the node is not traversed, and
154
+ * post is not called;
155
+ * - if pre returns a list of child nodes, only those will
156
+ * be traversed
157
+ * - otherwise all child nodes are traversed
158
+ *
159
+ * - if post returns false, the node it was called on is
160
+ * removed.
161
+ */
162
+ function ast_traverseAst(node, pre, post) {
163
+ const nodes = pre && pre(node);
164
+ if (nodes === false)
165
+ return;
166
+ if (!mctreeTypeInfo[node.type]) {
167
+ throw new Error("what?");
168
+ }
169
+ for (const key of nodes || mctreeTypeInfo[node.type]) {
170
+ const value = node[key];
171
+ if (!value)
172
+ continue;
173
+ if (Array.isArray(value)) {
174
+ const values = value;
175
+ const deletions = values.reduce((state, obj, i) => {
176
+ if (isMCTreeNode(obj)) {
177
+ const repl = ast_traverseAst(obj, pre, post);
178
+ if (repl === false) {
179
+ if (!state)
180
+ state = {};
181
+ state[i] = true;
182
+ }
183
+ else if (repl != null) {
184
+ if (!state)
185
+ state = {};
186
+ values[i] = repl;
187
+ }
188
+ }
189
+ return state;
190
+ }, null);
191
+ if (deletions) {
192
+ values.splice(0, values.length, ...values.filter((obj, i) => deletions[i] !== true).flat(1));
193
+ }
194
+ }
195
+ else if (isMCTreeNode(value)) {
196
+ const repl = ast_traverseAst(value, pre, post);
197
+ if (repl === false) {
198
+ delete node[key];
199
+ }
200
+ else if (repl != null) {
201
+ if (Array.isArray(repl)) {
202
+ throw new Error("Array returned by traverseAst in Node context");
203
+ }
204
+ node[key] = repl;
205
+ }
206
+ }
207
+ }
208
+ return post && post(node);
209
+ }
210
+
74
211
  ;// CONCATENATED MODULE: external "./api.cjs"
75
212
  const external_api_cjs_namespaceObject = require("./api.cjs");
76
213
  ;// CONCATENATED MODULE: ./src/variable-renamer.ts
77
214
 
215
+
78
216
  function variable_renamer_renameVariable(state, locals, declName) {
79
217
  const map = locals.map;
80
218
  if (!hasProperty(map, declName))
@@ -131,6 +269,7 @@ function variable_renamer_renameVariable(state, locals, declName) {
131
269
  ;// CONCATENATED MODULE: ./src/inliner.ts
132
270
 
133
271
 
272
+
134
273
  function getArgSafety(state, func, args, requireAll) {
135
274
  // determine whether decl might be changed by a function call
136
275
  // or assignment during the evaluation of FunctionStateNode.
@@ -198,25 +337,12 @@ function getArgSafety(state, func, args, requireAll) {
198
337
  if (allSafe && requireAll)
199
338
  return true;
200
339
  let callSeen = false;
201
- let ok = true;
202
340
  const params = Object.fromEntries(func.node.params.map((param, i) => [variableDeclarationName(param), i]));
203
- const getLoc = (node) => (Array.isArray(node) ? node[0].start : node.start) || 0;
204
341
  // look for uses of "unsafe" args that occur after a call.
205
342
  // use post to do the checking, because arguments are evaluated
206
343
  // prior to the call, so eg "return f(x.y);" is fine, but
207
344
  // "return f()+x.y" is not.
208
- //
209
- // We also have to use a "pre" to ensure that child nodes are
210
- // visited in source order (otherwise we could visit x.y before f()
211
- // in the above example)
212
- traverseAst(func.node.body, (node) => {
213
- return Object.entries(node)
214
- .filter((kv) => Array.isArray(kv[1])
215
- ? kv[1].length !== 0 && hasProperty(kv[1][0], "type")
216
- : hasProperty(kv[1], "type"))
217
- .sort(([, a], [, b]) => getLoc(a) - getLoc(b))
218
- .map(([key]) => key);
219
- }, (node) => {
345
+ traverseAst(func.node.body, null, (node) => {
220
346
  switch (node.type) {
221
347
  case "AssignmentExpression":
222
348
  case "UpdateExpression": {
@@ -726,6 +852,20 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
726
852
  return ["object"];
727
853
  }
728
854
  break;
855
+ case "MethodDefinition": {
856
+ if (!state.inType) {
857
+ throw new Error("Method definition outside of type!");
858
+ }
859
+ if (node.params) {
860
+ node.params.forEach((param) => {
861
+ if (param.type == "BinaryExpression") {
862
+ state.traverse(param.right);
863
+ state.inType = true;
864
+ }
865
+ });
866
+ }
867
+ return ["returnType"];
868
+ }
729
869
  }
730
870
  return null;
731
871
  };
@@ -741,6 +881,7 @@ function visitor_visitReferences(state, ast, name, defn, callback) {
741
881
 
742
882
 
743
883
 
884
+
744
885
  function collectClassInfo(state) {
745
886
  const toybox = state.stack[0].decls["Toybox"][0];
746
887
  const lang = toybox.decls["Lang"][0];
@@ -822,7 +963,9 @@ function getFileSources(fnMap) {
822
963
  fs
823
964
  .readFile(name)
824
965
  .then((data) => (value.monkeyCSource = data.toString().replace(/\r\n/g, "\n"))));
825
- })).then(() => { });
966
+ })).then(() => {
967
+ return;
968
+ });
826
969
  }
827
970
  function getFileASTs(fnMap) {
828
971
  return getFileSources(fnMap).then(() => Object.entries(fnMap).reduce((ok, [name, value]) => {
@@ -885,6 +1028,7 @@ async function analyze(fnMap, barrelList, config) {
885
1028
  node.body = null;
886
1029
  break;
887
1030
  }
1031
+ // falls through
888
1032
  case "ModuleDeclaration":
889
1033
  case "ClassDeclaration": {
890
1034
  const [scope] = state.stack.slice(-1);
@@ -928,7 +1072,7 @@ async function analyze(fnMap, barrelList, config) {
928
1072
  if (diagnosticType &&
929
1073
  !config?.compilerOptions?.includes("--Eno-invalid-symbol")) {
930
1074
  const checkTypes = config?.typeCheckLevel && config.typeCheckLevel !== "Off";
931
- Object.entries(fnMap).forEach(([k, v]) => {
1075
+ Object.entries(fnMap).forEach(([, v]) => {
932
1076
  visitReferences(state, v.ast, null, false, (node, results, error) => {
933
1077
  if (!error)
934
1078
  return undefined;
@@ -1273,7 +1417,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1273
1417
  case "ConditionalExpression":
1274
1418
  case "IfStatement":
1275
1419
  case "DoWhileStatement":
1276
- case "WhileStatement":
1420
+ case "WhileStatement": {
1277
1421
  const test = (state.traverse(node.test) ||
1278
1422
  node.test);
1279
1423
  const [value, type] = getNodeValue(test);
@@ -1305,6 +1449,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
1305
1449
  }
1306
1450
  }
1307
1451
  return null;
1452
+ }
1308
1453
  case "EnumDeclaration":
1309
1454
  return false;
1310
1455
  case "ForStatement": {
@@ -1739,6 +1884,8 @@ const external_sdk_util_cjs_namespaceObject = require("./sdk-util.cjs");
1739
1884
 
1740
1885
 
1741
1886
 
1887
+
1888
+
1742
1889
  /*
1743
1890
  * This is an unfortunate hack. I want to be able to extract things
1744
1891
  * like the types of all of a Class's variables (in particular the type
@@ -1786,8 +1933,9 @@ async function api_getApiMapping(state, barrelList) {
1786
1933
  return decls[0];
1787
1934
  }, result);
1788
1935
  const value = api_isStateNode(vs) ? vs.node : vs;
1789
- if (value.type !== "EnumStringMember" &&
1790
- (value.type !== "VariableDeclarator" || value.kind != "const")) {
1936
+ if (!value ||
1937
+ (value.type !== "EnumStringMember" &&
1938
+ (value.type !== "VariableDeclarator" || value.kind != "const"))) {
1791
1939
  throw `Negative constant ${fixup} did not refer to a constant`;
1792
1940
  }
1793
1941
  const init = getLiteralNode(value.init);
@@ -1835,8 +1983,9 @@ function checkOne(state, ns, decls, node, isStatic) {
1835
1983
  return null;
1836
1984
  }
1837
1985
  return cls.superClass.reduce((result, sup) => {
1838
- const next = api_hasProperty(sup[decls], node.name)
1839
- ? sup[decls][node.name]
1986
+ const sdecls = sup[decls];
1987
+ const next = api_hasProperty(sdecls, node.name)
1988
+ ? sdecls[node.name]
1840
1989
  : superChain(sup);
1841
1990
  return next ? (result ? result.concat(next) : next) : result;
1842
1991
  }, null);
@@ -1859,8 +2008,9 @@ function checkOne(state, ns, decls, node, isStatic) {
1859
2008
  }, null);
1860
2009
  };
1861
2010
  if (api_isStateNode(ns)) {
1862
- if (api_hasProperty(ns[decls], node.name)) {
1863
- return ns[decls][node.name];
2011
+ const ndecls = ns[decls];
2012
+ if (api_hasProperty(ndecls, node.name)) {
2013
+ return ndecls[node.name];
1864
2014
  }
1865
2015
  switch (ns.type) {
1866
2016
  case "ClassDeclaration":
@@ -2025,14 +2175,15 @@ function api_collectNamespaces(ast, stateIn) {
2025
2175
  let low = 0, high = ast.comments.length;
2026
2176
  while (high > low) {
2027
2177
  const mid = (low + high) >> 1;
2028
- if (ast.comments[mid].start < node.start) {
2178
+ if ((ast.comments[mid].start || 0) < node.start) {
2029
2179
  low = mid + 1;
2030
2180
  }
2031
2181
  else {
2032
2182
  high = mid;
2033
2183
  }
2034
2184
  }
2035
- while (high < ast.comments.length && ast.comments[high].end < node.end) {
2185
+ while (high < ast.comments.length &&
2186
+ (ast.comments[high].end || 0) < node.end) {
2036
2187
  high++;
2037
2188
  }
2038
2189
  if (high > low) {
@@ -2048,7 +2199,7 @@ function api_collectNamespaces(ast, stateIn) {
2048
2199
  ? { ...elm }
2049
2200
  : elm);
2050
2201
  state.inType = false;
2051
- state.traverse = (root) => api_traverseAst(root, (node) => {
2202
+ state.traverse = (root) => ast_traverseAst(root, (node) => {
2052
2203
  try {
2053
2204
  if (state.shouldExclude && state.shouldExclude(node)) {
2054
2205
  // don't visit any children, but do call post
@@ -2340,67 +2491,6 @@ function api_collectNamespaces(ast, stateIn) {
2340
2491
  }
2341
2492
  return state.stack[0];
2342
2493
  }
2343
- function isMCTreeNode(node) {
2344
- return node ? typeof node === "object" && "type" in node : false;
2345
- }
2346
- /*
2347
- * Traverse the ast rooted at node, calling pre before
2348
- * visiting each node, and post after.
2349
- *
2350
- * - if pre returns false, the node is not traversed, and
2351
- * post is not called;
2352
- * - if pre returns a list of child nodes, only those will
2353
- * be traversed
2354
- * - otherwise all child nodes are traversed
2355
- *
2356
- * - if post returns false, the node it was called on is
2357
- * removed.
2358
- */
2359
- function api_traverseAst(node, pre, post) {
2360
- const nodes = pre && pre(node);
2361
- if (nodes === false)
2362
- return;
2363
- for (const key of nodes || Object.keys(node)) {
2364
- const value = node[key];
2365
- if (!value)
2366
- continue;
2367
- if (Array.isArray(value)) {
2368
- const values = value;
2369
- const deletions = values.reduce((state, obj, i) => {
2370
- if (isMCTreeNode(obj)) {
2371
- const repl = api_traverseAst(obj, pre, post);
2372
- if (repl === false) {
2373
- if (!state)
2374
- state = {};
2375
- state[i] = true;
2376
- }
2377
- else if (repl != null) {
2378
- if (!state)
2379
- state = {};
2380
- values[i] = repl;
2381
- }
2382
- }
2383
- return state;
2384
- }, null);
2385
- if (deletions) {
2386
- values.splice(0, values.length, ...values.filter((obj, i) => deletions[i] !== true).flat(1));
2387
- }
2388
- }
2389
- else if (isMCTreeNode(value)) {
2390
- const repl = api_traverseAst(value, pre, post);
2391
- if (repl === false) {
2392
- delete node[key];
2393
- }
2394
- else if (repl != null) {
2395
- if (Array.isArray(repl)) {
2396
- throw new Error("Array returned by traverseAst in Node context");
2397
- }
2398
- node[key] = repl;
2399
- }
2400
- }
2401
- }
2402
- return post && post(node);
2403
- }
2404
2494
  function api_formatAst(node, monkeyCSource = null) {
2405
2495
  /*
2406
2496
  * The estree printer sometimes looks at the parent node without
@@ -2454,9 +2544,10 @@ function handleException(state, node, exception) {
2454
2544
  exception = new Error(message);
2455
2545
  }
2456
2546
  }
2457
- finally {
2547
+ catch (ex) {
2458
2548
  throw exception;
2459
2549
  }
2550
+ throw exception;
2460
2551
  }
2461
2552
  function findUsing(state, stack, using) {
2462
2553
  if (using.module)
@@ -2503,9 +2594,9 @@ function findUsingForNode(state, stack, i, node, isType) {
2503
2594
  for (let j = si.imports.length; j--;) {
2504
2595
  const using = si.imports[j];
2505
2596
  const module = findUsing(state, stack, using);
2506
- if (using.module) {
2507
- if (api_hasProperty(using.module.type_decls, node.name)) {
2508
- return using.module.type_decls[node.name];
2597
+ if (module) {
2598
+ if (api_hasProperty(module.type_decls, node.name)) {
2599
+ return module.type_decls[node.name];
2509
2600
  }
2510
2601
  }
2511
2602
  }
@@ -9858,7 +9858,7 @@ const external_util_cjs_namespaceObject = require("./util.cjs");
9858
9858
  async function build_project(product, options, lineCallback) {
9859
9859
  const { workspace, program, jungleFiles, developerKeyPath, simulatorBuild, releaseBuild, testBuild, compilerOptions, compilerWarnings, typeCheckLevel, returnCommand, } = options;
9860
9860
  const sdk = await (0,external_sdk_util_cjs_namespaceObject.getSdkPath)();
9861
- let extraArgs = [];
9861
+ const extraArgs = [];
9862
9862
  if (compilerOptions) {
9863
9863
  extraArgs.push(...compilerOptions.split(/\s+/));
9864
9864
  }
@@ -9894,6 +9894,9 @@ async function build_project(product, options, lineCallback) {
9894
9894
  else if (testBuild) {
9895
9895
  throw new Error("Building for tests requires a device to build for!");
9896
9896
  }
9897
+ if (!program || !jungleFiles || !developerKeyPath || !workspace) {
9898
+ throw new Error("Required arguments were missing!");
9899
+ }
9897
9900
  const exe = external_path_.resolve(sdk, "bin", external_sdk_util_cjs_namespaceObject.isWin ? "monkeyc.bat" : "monkeyc");
9898
9901
  const args = [
9899
9902
  ["-o", program],
@@ -9928,9 +9931,8 @@ async function readManifest(manifest) {
9928
9931
  return (0,xml2js.parseStringPromise)(data.toString(), { trim: true });
9929
9932
  }
9930
9933
  async function writeManifest(filename, xml) {
9931
- let builder = new xml2js.Builder();
9932
- let text = builder.buildObject(xml);
9933
- return promises_namespaceObject.writeFile(filename, text);
9934
+ const builder = new xml2js.Builder();
9935
+ return promises_namespaceObject.writeFile(filename, builder.buildObject(xml));
9934
9936
  }
9935
9937
  function manifestProducts(manifest) {
9936
9938
  const app = manifest["iq:manifest"]["iq:application"] ||
@@ -10228,7 +10230,7 @@ function resolve_node_by_path(state, path) {
10228
10230
  }
10229
10231
  if (!s[n] && s["."]) {
10230
10232
  const sdot = s["."];
10231
- let resolved = resolve_node_list(state, sdot);
10233
+ const resolved = resolve_node_list(state, sdot);
10232
10234
  if (!resolved.length)
10233
10235
  return undefined;
10234
10236
  const r = resolved[0][n];
@@ -10317,11 +10319,11 @@ async function resolve_literals(qualifier, default_source, deviceInfo) {
10317
10319
  return resolve_file_list(v.values);
10318
10320
  }
10319
10321
  let resolved = resolve_filename(v, default_source);
10320
- if (/[*?\[\]\{\}]/.test(resolved)) {
10322
+ if (/[*?[\]{}]/.test(resolved)) {
10321
10323
  // Jungle files can contain "./**.mc" which is supposed to match
10322
10324
  // any mc file under "./". The standard way to express that
10323
10325
  // is "./**/*.mc", which is what glob expects, so translate.
10324
- resolved = resolved.replace(/[\\\/]\*\*([^\\\/])/g, "/**/*$1");
10326
+ resolved = resolved.replace(/[\\/]\*\*([^\\/])/g, "/**/*$1");
10325
10327
  const match = await (0,external_util_cjs_namespaceObject.globa)(resolved);
10326
10328
  return match.length ? resolved : null;
10327
10329
  }
@@ -10595,7 +10597,7 @@ function resolve_barrel(barrel, barrelDir, products, options) {
10595
10597
  const sha1 = external_crypto_namespaceObject.createHash("sha1")
10596
10598
  .update(barrel, "binary")
10597
10599
  .digest("base64")
10598
- .replace(/[\/=+]/g, "");
10600
+ .replace(/[/=+]/g, "");
10599
10601
  const localPath = external_path_.resolve(barrelDir, `${external_path_.basename(barrel, ".barrel")}-${sha1}`);
10600
10602
  rawBarrel = external_path_.resolve(localPath, "barrel.jungle");
10601
10603
  promise = promise.then(() => promises_namespaceObject.stat(localPath)
@@ -10745,7 +10747,9 @@ async function get_jungle_and_barrels(jungleFiles, defaultProducts, options) {
10745
10747
  targets.push({ product, qualifier, shape });
10746
10748
  return resolve_barrels(product, qualifier, barrels, products, options);
10747
10749
  })
10748
- .then(() => { });
10750
+ .then(() => {
10751
+ return;
10752
+ });
10749
10753
  };
10750
10754
  products.forEach((product) => {
10751
10755
  if ((0,external_api_cjs_namespaceObject.hasProperty)(state, product)) {
@@ -10788,11 +10792,151 @@ function simulateProgram(prg, device, test = false, logger) {
10788
10792
  const args = [prg, device];
10789
10793
  if (test)
10790
10794
  args.push("-t");
10791
- return (0,external_sdk_util_cjs_namespaceObject.getSdkPath)().then((sdk) => (0,external_util_cjs_namespaceObject.spawnByLine)(external_path_.resolve(sdk, "bin", "monkeydo"), args, logger || ((line) => console.log(line))).then(() => { }));
10795
+ return (0,external_sdk_util_cjs_namespaceObject.getSdkPath)().then((sdk) => (0,external_util_cjs_namespaceObject.spawnByLine)(external_path_.resolve(sdk, "bin", "monkeydo"), args, logger || ((line) => console.log(line))).then(() => {
10796
+ return;
10797
+ }));
10798
+ }
10799
+
10800
+ ;// CONCATENATED MODULE: ./src/ast.ts
10801
+ /*
10802
+ * This ensures that mctreeTypeInfo has every key of MCTreeTypeInfo,
10803
+ * and that the corresponding arrays contain every element of the
10804
+ * corresponding type.
10805
+ *
10806
+ * ie, any time mctree.Node changes, we'll get errors here if
10807
+ * mctreeTypeInfo needs updating.
10808
+ *
10809
+ */
10810
+ function _check(x) {
10811
+ const y = x;
10812
+ x = y;
10813
+ }
10814
+ const mctreeTypeInfo = {
10815
+ ArrayExpression: ["elements"],
10816
+ AssignmentExpression: ["left", "right"],
10817
+ AttributeList: ["attributes"],
10818
+ Attributes: ["elements"],
10819
+ BinaryExpression: ["left", "right"],
10820
+ Block: [],
10821
+ BlockStatement: ["body", "innerComments"],
10822
+ BreakStatement: [],
10823
+ CallExpression: ["callee", "arguments"],
10824
+ CatchClause: ["param", "body"],
10825
+ CatchClauses: ["catches"],
10826
+ ClassBody: ["body"],
10827
+ ClassDeclaration: ["attrs", "id", "superClass", "body"],
10828
+ ClassElement: ["item"],
10829
+ ConditionalExpression: ["test", "consequent", "alternate"],
10830
+ ContinueStatement: [],
10831
+ DoWhileStatement: ["body", "test"],
10832
+ EnumDeclaration: ["attrs", "id", "body"],
10833
+ EnumStringBody: ["members"],
10834
+ EnumStringMember: ["id", "init"],
10835
+ ExpressionStatement: ["expression"],
10836
+ ForStatement: ["init", "test", "body", "update"],
10837
+ FunctionDeclaration: ["attrs", "id", "params", "body"],
10838
+ Identifier: [],
10839
+ IfStatement: ["test", "consequent", "alternate"],
10840
+ ImportModule: ["id"],
10841
+ InstanceOfCase: ["id"],
10842
+ Line: [],
10843
+ Literal: [],
10844
+ LogicalExpression: ["left", "right"],
10845
+ MemberExpression: ["object", "property"],
10846
+ MethodDefinition: ["params", "returnType"],
10847
+ ModuleDeclaration: ["attrs", "id", "body"],
10848
+ MultiLine: [],
10849
+ NewExpression: ["callee", "arguments"],
10850
+ ObjectExpression: ["properties"],
10851
+ ParenthesizedExpression: ["expression"],
10852
+ Program: ["body", "comments"],
10853
+ Property: ["key", "value"],
10854
+ ReturnStatement: ["argument"],
10855
+ SequenceExpression: ["expressions"],
10856
+ SizedArrayExpression: ["size", "ts"],
10857
+ SwitchCase: ["test", "consequent"],
10858
+ SwitchStatement: ["discriminant", "cases"],
10859
+ ThisExpression: [],
10860
+ ThrowStatement: ["argument"],
10861
+ TryStatement: ["block", "handler", "finalizer"],
10862
+ TypedefDeclaration: ["attrs", "id", "ts"],
10863
+ TypeSpecList: ["ts"],
10864
+ TypeSpecPart: ["body", "callspec", "generics"],
10865
+ UnaryExpression: ["argument"],
10866
+ UpdateExpression: ["argument"],
10867
+ Using: ["id", "as"],
10868
+ VariableDeclaration: ["attrs", "declarations"],
10869
+ VariableDeclarator: ["id", "init"],
10870
+ WhileStatement: ["test", "body"],
10871
+ };
10872
+ function isMCTreeNode(node) {
10873
+ return node ? typeof node === "object" && "type" in node : false;
10874
+ }
10875
+ /*
10876
+ * Traverse the ast rooted at node, calling pre before
10877
+ * visiting each node, and post after.
10878
+ *
10879
+ * - if pre returns false, the node is not traversed, and
10880
+ * post is not called;
10881
+ * - if pre returns a list of child nodes, only those will
10882
+ * be traversed
10883
+ * - otherwise all child nodes are traversed
10884
+ *
10885
+ * - if post returns false, the node it was called on is
10886
+ * removed.
10887
+ */
10888
+ function traverseAst(node, pre, post) {
10889
+ const nodes = pre && pre(node);
10890
+ if (nodes === false)
10891
+ return;
10892
+ if (!mctreeTypeInfo[node.type]) {
10893
+ throw new Error("what?");
10894
+ }
10895
+ for (const key of nodes || mctreeTypeInfo[node.type]) {
10896
+ const value = node[key];
10897
+ if (!value)
10898
+ continue;
10899
+ if (Array.isArray(value)) {
10900
+ const values = value;
10901
+ const deletions = values.reduce((state, obj, i) => {
10902
+ if (isMCTreeNode(obj)) {
10903
+ const repl = traverseAst(obj, pre, post);
10904
+ if (repl === false) {
10905
+ if (!state)
10906
+ state = {};
10907
+ state[i] = true;
10908
+ }
10909
+ else if (repl != null) {
10910
+ if (!state)
10911
+ state = {};
10912
+ values[i] = repl;
10913
+ }
10914
+ }
10915
+ return state;
10916
+ }, null);
10917
+ if (deletions) {
10918
+ values.splice(0, values.length, ...values.filter((obj, i) => deletions[i] !== true).flat(1));
10919
+ }
10920
+ }
10921
+ else if (isMCTreeNode(value)) {
10922
+ const repl = traverseAst(value, pre, post);
10923
+ if (repl === false) {
10924
+ delete node[key];
10925
+ }
10926
+ else if (repl != null) {
10927
+ if (Array.isArray(repl)) {
10928
+ throw new Error("Array returned by traverseAst in Node context");
10929
+ }
10930
+ node[key] = repl;
10931
+ }
10932
+ }
10933
+ }
10934
+ return post && post(node);
10792
10935
  }
10793
10936
 
10794
10937
  ;// CONCATENATED MODULE: ./src/variable-renamer.ts
10795
10938
 
10939
+
10796
10940
  function renameVariable(state, locals, declName) {
10797
10941
  const map = locals.map;
10798
10942
  if (!(0,external_api_cjs_namespaceObject.hasProperty)(map, declName))
@@ -10809,7 +10953,7 @@ function renameVariable(state, locals, declName) {
10809
10953
  // more conflicts
10810
10954
  locals.inners = {};
10811
10955
  const inners = locals.inners;
10812
- (0,external_api_cjs_namespaceObject.traverseAst)(locals.node, (node) => {
10956
+ traverseAst(locals.node, (node) => {
10813
10957
  if (node.type === "VariableDeclarator") {
10814
10958
  inners[(0,external_api_cjs_namespaceObject.variableDeclarationName)(node.id)] = true;
10815
10959
  }
@@ -10849,6 +10993,7 @@ function renameVariable(state, locals, declName) {
10849
10993
  ;// CONCATENATED MODULE: ./src/inliner.ts
10850
10994
 
10851
10995
 
10996
+
10852
10997
  function getArgSafety(state, func, args, requireAll) {
10853
10998
  // determine whether decl might be changed by a function call
10854
10999
  // or assignment during the evaluation of FunctionStateNode.
@@ -10916,25 +11061,12 @@ function getArgSafety(state, func, args, requireAll) {
10916
11061
  if (allSafe && requireAll)
10917
11062
  return true;
10918
11063
  let callSeen = false;
10919
- let ok = true;
10920
11064
  const params = Object.fromEntries(func.node.params.map((param, i) => [(0,external_api_cjs_namespaceObject.variableDeclarationName)(param), i]));
10921
- const getLoc = (node) => (Array.isArray(node) ? node[0].start : node.start) || 0;
10922
11065
  // look for uses of "unsafe" args that occur after a call.
10923
11066
  // use post to do the checking, because arguments are evaluated
10924
11067
  // prior to the call, so eg "return f(x.y);" is fine, but
10925
11068
  // "return f()+x.y" is not.
10926
- //
10927
- // We also have to use a "pre" to ensure that child nodes are
10928
- // visited in source order (otherwise we could visit x.y before f()
10929
- // in the above example)
10930
- (0,external_api_cjs_namespaceObject.traverseAst)(func.node.body, (node) => {
10931
- return Object.entries(node)
10932
- .filter((kv) => Array.isArray(kv[1])
10933
- ? kv[1].length !== 0 && (0,external_api_cjs_namespaceObject.hasProperty)(kv[1][0], "type")
10934
- : (0,external_api_cjs_namespaceObject.hasProperty)(kv[1], "type"))
10935
- .sort(([, a], [, b]) => getLoc(a) - getLoc(b))
10936
- .map(([key]) => key);
10937
- }, (node) => {
11069
+ traverseAst(func.node.body, null, (node) => {
10938
11070
  switch (node.type) {
10939
11071
  case "AssignmentExpression":
10940
11072
  case "UpdateExpression": {
@@ -11207,7 +11339,7 @@ function inlineWithArgs(state, func, call, context) {
11207
11339
  }
11208
11340
  }
11209
11341
  else {
11210
- (0,external_api_cjs_namespaceObject.traverseAst)(func.node.body, (node) => {
11342
+ traverseAst(func.node.body, (node) => {
11211
11343
  node.type === "ReturnStatement" && retStmtCount++;
11212
11344
  });
11213
11345
  if (retStmtCount > 1) {
@@ -11442,6 +11574,20 @@ function visitReferences(state, ast, name, defn, callback) {
11442
11574
  return ["object"];
11443
11575
  }
11444
11576
  break;
11577
+ case "MethodDefinition": {
11578
+ if (!state.inType) {
11579
+ throw new Error("Method definition outside of type!");
11580
+ }
11581
+ if (node.params) {
11582
+ node.params.forEach((param) => {
11583
+ if (param.type == "BinaryExpression") {
11584
+ state.traverse(param.right);
11585
+ state.inType = true;
11586
+ }
11587
+ });
11588
+ }
11589
+ return ["returnType"];
11590
+ }
11445
11591
  }
11446
11592
  return null;
11447
11593
  };
@@ -11457,6 +11603,7 @@ function visitReferences(state, ast, name, defn, callback) {
11457
11603
 
11458
11604
 
11459
11605
 
11606
+
11460
11607
  function collectClassInfo(state) {
11461
11608
  const toybox = state.stack[0].decls["Toybox"][0];
11462
11609
  const lang = toybox.decls["Lang"][0];
@@ -11537,7 +11684,9 @@ function getFileSources(fnMap) {
11537
11684
  return (value.monkeyCSource ||
11538
11685
  promises_namespaceObject.readFile(name)
11539
11686
  .then((data) => (value.monkeyCSource = data.toString().replace(/\r\n/g, "\n"))));
11540
- })).then(() => { });
11687
+ })).then(() => {
11688
+ return;
11689
+ });
11541
11690
  }
11542
11691
  function getFileASTs(fnMap) {
11543
11692
  return getFileSources(fnMap).then(() => Object.entries(fnMap).reduce((ok, [name, value]) => {
@@ -11600,6 +11749,7 @@ async function analyze(fnMap, barrelList, config) {
11600
11749
  node.body = null;
11601
11750
  break;
11602
11751
  }
11752
+ // falls through
11603
11753
  case "ModuleDeclaration":
11604
11754
  case "ClassDeclaration": {
11605
11755
  const [scope] = state.stack.slice(-1);
@@ -11643,7 +11793,7 @@ async function analyze(fnMap, barrelList, config) {
11643
11793
  if (diagnosticType &&
11644
11794
  !config?.compilerOptions?.includes("--Eno-invalid-symbol")) {
11645
11795
  const checkTypes = config?.typeCheckLevel && config.typeCheckLevel !== "Off";
11646
- Object.entries(fnMap).forEach(([k, v]) => {
11796
+ Object.entries(fnMap).forEach(([, v]) => {
11647
11797
  visitReferences(state, v.ast, null, false, (node, results, error) => {
11648
11798
  if (!error)
11649
11799
  return undefined;
@@ -11849,7 +11999,7 @@ function evaluateFunction(func, args) {
11849
11999
  ? JSON.parse(JSON.stringify(func.body))
11850
12000
  : func.body;
11851
12001
  try {
11852
- (0,external_api_cjs_namespaceObject.traverseAst)(body, (node) => {
12002
+ traverseAst(body, (node) => {
11853
12003
  switch (node.type) {
11854
12004
  case "BlockStatement":
11855
12005
  case "ReturnStatement":
@@ -11988,7 +12138,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
11988
12138
  case "ConditionalExpression":
11989
12139
  case "IfStatement":
11990
12140
  case "DoWhileStatement":
11991
- case "WhileStatement":
12141
+ case "WhileStatement": {
11992
12142
  const test = (state.traverse(node.test) ||
11993
12143
  node.test);
11994
12144
  const [value, type] = getNodeValue(test);
@@ -12020,6 +12170,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
12020
12170
  }
12021
12171
  }
12022
12172
  return null;
12173
+ }
12023
12174
  case "EnumDeclaration":
12024
12175
  return false;
12025
12176
  case "ForStatement": {
@@ -12331,7 +12482,7 @@ async function optimizeMonkeyC(fnMap, barrelList, config) {
12331
12482
  return null;
12332
12483
  };
12333
12484
  Object.values(fnMap).forEach((f) => {
12334
- (0,external_api_cjs_namespaceObject.traverseAst)(f.ast, undefined, (node) => {
12485
+ traverseAst(f.ast, undefined, (node) => {
12335
12486
  const ret = cleanup(node);
12336
12487
  if (ret === false) {
12337
12488
  state.removeNodeComments(node, f.ast);
@@ -12388,6 +12539,7 @@ function optimizeCall(state, node, context) {
12388
12539
 
12389
12540
  ;// CONCATENATED MODULE: ./src/pragma-checker.ts
12390
12541
 
12542
+
12391
12543
  function pragmaChecker(ast, diagnostics) {
12392
12544
  const comments = ast.comments;
12393
12545
  if (!comments)
@@ -12434,7 +12586,7 @@ function pragmaChecker(ast, diagnostics) {
12434
12586
  return re.test(haystack);
12435
12587
  };
12436
12588
  next();
12437
- (0,external_api_cjs_namespaceObject.traverseAst)(ast, (node) => {
12589
+ traverseAst(ast, (node) => {
12438
12590
  if (index >= comments.length)
12439
12591
  return false;
12440
12592
  if (node.start && node.start >= (comment.end || Infinity)) {
@@ -12513,7 +12665,7 @@ function pragmaChecker(ast, diagnostics) {
12513
12665
 
12514
12666
 
12515
12667
  function relative_path_no_dotdot(relative) {
12516
- return relative.replace(/^(\.\.[\\\/])+/, (str) => `__${"dot".repeat(str.length / 3)}__${str.slice(-1)}`);
12668
+ return relative.replace(/^(\.\.[\\/])+/, (str) => `__${"dot".repeat(str.length / 3)}__${str.slice(-1)}`);
12517
12669
  }
12518
12670
  async function getVSCodeSettings(path) {
12519
12671
  try {
@@ -12645,7 +12797,7 @@ async function createLocalBarrels(targets, options) {
12645
12797
  const sha1 = external_crypto_namespaceObject.createHash("sha1")
12646
12798
  .update(rawBarrelDir, "binary")
12647
12799
  .digest("base64")
12648
- .replace(/[\/=+]/g, "");
12800
+ .replace(/[/=+]/g, "");
12649
12801
  const optBarrelDir = external_path_.resolve(barrelDir, `${barrel}-${sha1}`);
12650
12802
  if (!(0,external_api_cjs_namespaceObject.hasProperty)(optBarrels, barrel)) {
12651
12803
  optBarrels[barrel] = {
@@ -12693,7 +12845,9 @@ async function generateOptimizedProject(options) {
12693
12845
  }
12694
12846
  return {
12695
12847
  jungleFiles: config.jungleFiles,
12848
+ xml,
12696
12849
  program: external_path_.basename(external_path_.dirname(manifest)),
12850
+ hasTests: !!config.testBuild,
12697
12851
  };
12698
12852
  }
12699
12853
  let dropBarrels = false;
@@ -12794,7 +12948,7 @@ async function generateOptimizedProject(options) {
12794
12948
  }
12795
12949
  const prefix = `${product}.`;
12796
12950
  process_field(prefix, qualifier, "sourcePath", (s) => external_path_.join(group.dir, "source", relative_path_no_dotdot(external_path_.relative(workspace, s)))
12797
- .replace(/([\\\/]\*\*)[\\\/]\*/g, "$1"));
12951
+ .replace(/([\\/]\*\*)[\\/]\*/g, "$1"));
12798
12952
  if (group.optimizerConfig.optBarrels) {
12799
12953
  parts.push(`${prefix}barrelPath = ${Object.values(group.optimizerConfig.optBarrels)
12800
12954
  .map((value) => `[${value.jungleFiles
@@ -12808,7 +12962,7 @@ async function generateOptimizedProject(options) {
12808
12962
  .map(([barrel, resolvedBarrel]) => {
12809
12963
  const root = external_path_.dirname(resolvedBarrel.jungles[0]);
12810
12964
  return (resolvedBarrel.qualifier.sourcePath || []).map((s) => external_path_.join(group.dir, "barrels", barrel, external_path_.relative(root, s))
12811
- .replace(/([\\\/]\*\*)[\\\/]\*/g, "$1"));
12965
+ .replace(/([\\/]\*\*)[\\/]\*/g, "$1"));
12812
12966
  })
12813
12967
  .flat()
12814
12968
  .sort()
@@ -12931,7 +13085,7 @@ async function generateOneConfig(buildConfig, dependencyFiles, config) {
12931
13085
  // the oldest optimized file, we don't need to regenerate
12932
13086
  const source_time = await (0,external_util_cjs_namespaceObject.last_modified)(Object.keys(fnMap).concat(dependencyFiles));
12933
13087
  const opt_time = await (0,external_util_cjs_namespaceObject.first_modified)(Object.values(fnMap).map((v) => v.output));
12934
- if (source_time < opt_time && 1655479110007 < opt_time) {
13088
+ if (source_time < opt_time && 1655677006664 < opt_time) {
12935
13089
  return { hasTests, diagnostics: prevDiagnostics };
12936
13090
  }
12937
13091
  }
@@ -1,5 +1,7 @@
1
1
  import { mctree } from "@markw65/prettier-plugin-monkeyc";
2
+ import { traverseAst } from "./ast";
2
3
  export { visitReferences } from "./visitor";
4
+ export { traverseAst };
3
5
  export declare function getApiMapping(state?: ProgramState, barrelList?: string[]): Promise<ProgramStateNode | null>;
4
6
  export declare function hasProperty<T extends null extends T ? unknown : undefined extends T ? unknown : never>(obj: T, prop: string): obj is NonNullable<T>;
5
7
  export declare function hasProperty<T>(obj: T, prop: string): boolean;
@@ -7,6 +9,5 @@ export declare function isStateNode(node: StateNodeDecl): node is StateNode;
7
9
  export declare function variableDeclarationName(node: mctree.TypedIdentifier | mctree.InstanceofIdentifier): string;
8
10
  export declare function sameLookupResult(a: LookupDefinition[], b: LookupDefinition[]): boolean;
9
11
  export declare function collectNamespaces(ast: mctree.Program, stateIn?: ProgramState): ProgramStateNode;
10
- export declare function traverseAst(node: mctree.Node, pre?: null | ((node: mctree.Node) => void | null | false | (keyof mctree.NodeAll)[]), post?: (node: mctree.Node) => void | null | false | mctree.Node | mctree.Node[]): false | void | null | mctree.Node | mctree.Node[];
11
12
  export declare function formatAst(node: mctree.Node, monkeyCSource?: string | null): string;
12
13
  export declare function findUsingForNode(state: ProgramStateLive, stack: ProgramStateStack, i: number, node: mctree.Identifier, isType: boolean): StateNodeDecl[] | null;
@@ -0,0 +1,2 @@
1
+ import { mctree } from "@markw65/prettier-plugin-monkeyc";
2
+ export declare function traverseAst(node: mctree.Node, pre?: null | ((node: mctree.Node) => void | null | false | (keyof mctree.NodeAll)[]), post?: (node: mctree.Node) => void | null | false | mctree.Node | mctree.Node[]): false | void | null | mctree.Node | mctree.Node[];
@@ -197,7 +197,7 @@ export declare function buildOptimizedProject(product: string | null, options: B
197
197
  args: string[];
198
198
  program: string;
199
199
  product: string | null;
200
- hasTests: boolean | undefined;
200
+ hasTests: boolean;
201
201
  diagnostics: Record<string, {
202
202
  type: DiagnosticType;
203
203
  loc: {
@@ -209,9 +209,9 @@ export declare function buildOptimizedProject(product: string | null, options: B
209
209
  }>;
210
210
  export declare function generateOptimizedProject(options: BuildConfig): Promise<{
211
211
  jungleFiles: string | undefined;
212
+ xml: import("./manifest").ManifestXML;
212
213
  program: string;
213
- xml?: undefined;
214
- hasTests?: undefined;
214
+ hasTests: boolean;
215
215
  diagnostics?: undefined;
216
216
  } | {
217
217
  jungleFiles: string;
package/build/util.cjs CHANGED
@@ -4145,10 +4145,12 @@ async function copyRecursiveAsNeeded(source, target, filter) {
4145
4145
  }
4146
4146
  const files = await promises_namespaceObject.readdir(source);
4147
4147
  return Promise.all(files.map((file) => {
4148
- var src = external_path_.join(source, file);
4149
- var tgt = external_path_.join(target, file);
4148
+ const src = external_path_.join(source, file);
4149
+ const tgt = external_path_.join(target, file);
4150
4150
  return copyRecursiveAsNeeded(src, tgt, filter);
4151
- })).then(() => { });
4151
+ })).then(() => {
4152
+ return;
4153
+ });
4152
4154
  }
4153
4155
  else {
4154
4156
  if (filter && !filter(source, target)) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@markw65/monkeyc-optimizer",
3
3
  "type": "module",
4
- "version": "1.0.22",
4
+ "version": "1.0.23",
5
5
  "description": "Source to source optimizer for Garmin Monkey C code",
6
6
  "main": "build/optimizer.cjs",
7
7
  "types": "build/src/optimizer.d.ts",
@@ -20,9 +20,12 @@
20
20
  "build-debug": "webpack --mode development",
21
21
  "build-release": "webpack --mode production",
22
22
  "prepack": "webpack --mode production",
23
- "test": "npm run test-inline && npm run test-remote",
23
+ "test": "npm run test-optimized && npm run test-remote",
24
24
  "test-remote": "node ./test/test.js --product=pick-one --github",
25
- "test-inline": "node test/test.js --run-tests --product=fenix5 --product=fr235 --jungle $(pwd)/test/OptimizerTests/monkey.jungle"
25
+ "test-optimized": "node test/test.js --run-tests --product=fenix5 --product=fr235 --jungle $(pwd)/test/OptimizerTests/monkey.jungle",
26
+ "test-unopt": "node test/test.js --skipOptimization --run-tests --product=fenix5 --product=fr235 --jungle $(pwd)/test/OptimizerTests/monkey.jungle",
27
+ "test-garmin-opt": "node test/test.js --skipOptimization --garminOptLevel=2 --run-tests --product=fenix5 --product=fr235 --jungle $(pwd)/test/OptimizerTests/monkey.jungle",
28
+ "eslint": "npx eslint ."
26
29
  },
27
30
  "files": [
28
31
  "build/optimizer.cjs",
@@ -34,12 +37,14 @@
34
37
  "author": "markw65",
35
38
  "license": "MIT",
36
39
  "dependencies": {
37
- "@markw65/prettier-plugin-monkeyc": "^1.0.26"
40
+ "@markw65/prettier-plugin-monkeyc": "^1.0.27"
38
41
  },
39
42
  "devDependencies": {
40
43
  "@types/glob": "^7.2.0",
41
44
  "@types/prettier": "^2.6.1",
42
45
  "@types/xml2js": "^0.4.11",
46
+ "@typescript-eslint/eslint-plugin": "^5.28.0",
47
+ "@typescript-eslint/parser": "^5.28.0",
43
48
  "eslint": "^8.12.0",
44
49
  "extract-zip": "^2.0.1",
45
50
  "glob": "^7.2.0",