vba-runner 0.1.1-alpha.0 → 0.1.1-alpha.1

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.
@@ -41,6 +41,7 @@ __export(vba_analyzer_exports, {
41
41
  formatWorkspaceOutlineForMcp: () => formatWorkspaceOutline,
42
42
  formatWorkspaceReportForMcp: () => formatWorkspaceReportForMcp,
43
43
  formatWorkspaceSummary: () => formatWorkspaceSummary,
44
+ main: () => main,
44
45
  paramName: () => paramName,
45
46
  vbaTypeToTs: () => vbaTypeToTs
46
47
  });
@@ -591,25 +592,46 @@ var Parser = class _Parser {
591
592
  ..._Parser.CONTEXTUAL_KW_STMT_ABSENT,
592
593
  ..._Parser.CONTEXTUAL_KW_STRUCTURAL
593
594
  ]);
594
- /** <statement-keyword> tokens additionally permitted as IDENTIFIERs in
595
- * expression context (parsePrimary) for practical VBA compatibility —
596
- * e.g. as method names (obj.Print, ws.Get) or file I/O targets.
597
- * These remain <reserved-identifier> per §3.3.5.2. */
595
+ /** <statement-keyword> tokens that are <reserved-identifier> per §3.3.5.2 but whose
596
+ * enum values fall in [KeywordBase, KeywordAddressOf].
597
+ * These CANNOT be used as module-level procedure names (parseProcedureDeclaration rejects them).
598
+ * They ARE permitted as member names in expression context (obj.Print, ws.Get, obj.Open) via
599
+ * parsePrimary, which is why they appear in COMPAT_KW_EXPR. */
600
+ static STATEMENT_KW_RESERVED = /* @__PURE__ */ new Set([
601
+ 81 /* KeywordOpen */,
602
+ 82 /* KeywordClose */,
603
+ 84 /* KeywordInput */,
604
+ 85 /* KeywordPrint */,
605
+ 86 /* KeywordPut */,
606
+ 92 /* KeywordWrite */,
607
+ 93 /* KeywordLock */,
608
+ 98 /* KeywordSeek */,
609
+ 100 /* KeywordUnlock */,
610
+ 102 /* KeywordEvent */,
611
+ 103 /* KeywordRaiseEvent */,
612
+ 105 /* KeywordImplements */
613
+ ]);
614
+ /** <statement-keyword> tokens permitted as member names in parsePrimary (obj.Print, ws.Get,
615
+ * obj.Open, etc.) per §3.3.5.2 "unrestricted-name" rule.
616
+ * In parseStatementInner these keywords are all dispatched unconditionally before the
617
+ * identifier branch, so the COMPAT_KW_EXPR entries there are never reached — they exist
618
+ * solely for parsePrimary dot-member-access context. */
598
619
  static COMPAT_KW_EXPR = /* @__PURE__ */ new Set([
599
620
  98 /* KeywordSeek */,
600
- // <statement-keyword>
601
621
  84 /* KeywordInput */,
602
- // <statement-keyword> / <special-form>
603
622
  85 /* KeywordPrint */,
604
- // <statement-keyword>
605
623
  86 /* KeywordPut */,
606
- // <statement-keyword>
607
624
  20 /* KeywordGet */,
608
- // <statement-keyword>
625
+ // enum < KeywordBase so also rejected by range check in parseProcedureDeclaration
609
626
  93 /* KeywordLock */,
610
- // <statement-keyword>
611
- 100 /* KeywordUnlock */
612
- // <statement-keyword>
627
+ 100 /* KeywordUnlock */,
628
+ 81 /* KeywordOpen */,
629
+ 82 /* KeywordClose */,
630
+ // Non-reserved statement keywords: can be user-defined proc names (e.g. Function Kill()).
631
+ // Included here so "Kill = v", "Reset = v", "Width = v" fall through to identifier branch.
632
+ 97 /* KeywordKill */,
633
+ 99 /* KeywordReset */,
634
+ 109 /* KeywordWidth */
613
635
  ]);
614
636
  errorRecovery;
615
637
  /** Returns true if token is a valid IDENTIFIER per §3.3.5.2:
@@ -626,6 +648,15 @@ var Parser = class _Parser {
626
648
  isNameToken(token) {
627
649
  return token.type === 0 /* Identifier */ || token.type === 138 /* ForeignName */ || token.type >= 3 /* KeywordFor */ && token.type <= 110 /* KeywordAddressOf */;
628
650
  }
651
+ /**
652
+ * Returns true if the current token stream contains a file-I/O Open statement pattern
653
+ * ("For <file-mode>") on the current logical line.
654
+ * Open "path" For Input|Output|Append|Random|Binary ... As #n → true
655
+ * Open() / Open = value / Open x (user-defined call) → false
656
+ *
657
+ * This syntactic check lets the parser disambiguate without a pre-scan:
658
+ * VBA §3.3.5.3 — user-defined procedures (priority 2) > built-in statement keywords (priority 3).
659
+ */
629
660
  recordError(message, token) {
630
661
  const pos = { line: token.line, column: token.column };
631
662
  this._diagnostics.push({ message, loc: { start: pos, end: pos }, severity: "error" });
@@ -1116,6 +1147,67 @@ var Parser = class _Parser {
1116
1147
  }
1117
1148
  return stmt;
1118
1149
  }
1150
+ /**
1151
+ * Parse an identifier, method call, or assignment statement.
1152
+ * Called from the identifier branch of parseStatementInner AND from the ARCH-1
1153
+ * user-proc override path (when a built-in keyword is user-defined as a procedure).
1154
+ */
1155
+ parseIdentifierOrCallStatement() {
1156
+ const token = this.peek();
1157
+ if ((token.type === 0 /* Identifier */ || _Parser.CONTEXTUAL_KW.has(token.type)) && this.pos + 1 < this.tokens.length && this.tokens[this.pos + 1].type === 130 /* OperatorColon */) {
1158
+ const labelName = token.value;
1159
+ this.advance();
1160
+ this.advance();
1161
+ return { type: "LabelStatement", label: labelName };
1162
+ }
1163
+ if (token.type === 1 /* Number */) {
1164
+ const labelName = token.value;
1165
+ this.advance();
1166
+ this.match(130 /* OperatorColon */);
1167
+ return { type: "LabelStatement", label: labelName };
1168
+ }
1169
+ const savedPos = this.pos;
1170
+ const expr = this.parsePrimary();
1171
+ if (this.match(120 /* OperatorEquals */)) {
1172
+ return {
1173
+ type: "AssignmentStatement",
1174
+ left: expr,
1175
+ right: this.parseExpression()
1176
+ };
1177
+ } else {
1178
+ if (expr.type === "CallExpression" && this.isBinaryOnlyOperator(this.peek().type)) {
1179
+ this.pos = savedPos;
1180
+ const callee = this.parsePrimary(
1181
+ /* stopBeforeSpacedLParen= */
1182
+ true
1183
+ );
1184
+ const args2 = [this.parseCallArgument()];
1185
+ while (this.match(126 /* OperatorComma */)) {
1186
+ args2.push(this.parseCallArgument());
1187
+ }
1188
+ return { type: "CallStatement", expression: { type: "CallExpression", callee, args: args2 } };
1189
+ }
1190
+ const args = [];
1191
+ if (this.peek().type !== 135 /* Newline */ && this.peek().type !== 136 /* EOF */ && this.peek().type !== 9 /* KeywordElse */ && this.peek().type !== 8 /* KeywordElseIf */ && this.peek().type !== 10 /* KeywordEnd */ && this.peek().type !== 5 /* KeywordNext */ && this.peek().type !== 14 /* KeywordLoop */) {
1192
+ args.push(this.parseCallArgument());
1193
+ while (this.match(126 /* OperatorComma */)) {
1194
+ args.push(this.parseCallArgument());
1195
+ }
1196
+ }
1197
+ if (args.length > 0) {
1198
+ return { type: "CallStatement", expression: { type: "CallExpression", callee: expr, args } };
1199
+ } else if (expr.type === "CallExpression") {
1200
+ const callExpr = expr;
1201
+ if (callExpr.args.length === 0 && callExpr.callee.type === "Identifier" && !callExpr.callee.foreign) {
1202
+ const line = callExpr.loc?.start.line ?? this.peek().line;
1203
+ this.throwError(`Parse error: syntax error at line ${line}`);
1204
+ }
1205
+ return { type: "CallStatement", expression: callExpr };
1206
+ } else {
1207
+ return { type: "CallStatement", expression: { type: "CallExpression", callee: expr, args: [] } };
1208
+ }
1209
+ }
1210
+ }
1119
1211
  parseStatementInner() {
1120
1212
  const token = this.peek();
1121
1213
  if (_Parser.CONTEXTUAL_KW.has(token.type) && this.pos + 1 < this.tokens.length && this.tokens[this.pos + 1].type === 130 /* OperatorColon */) {
@@ -1150,6 +1242,9 @@ var Parser = class _Parser {
1150
1242
  stmt2.scope = scope;
1151
1243
  return stmt2;
1152
1244
  }
1245
+ if (next.type === 102 /* KeywordEvent */) {
1246
+ return this.parseEventDeclaration(scope);
1247
+ }
1153
1248
  const stmt = this.parseDimStatement(false, true);
1154
1249
  if (stmt) {
1155
1250
  stmt.scope = scope;
@@ -1271,7 +1366,7 @@ var Parser = class _Parser {
1271
1366
  return this.parsePrintStatement();
1272
1367
  } else if (token.type === 86 /* KeywordPut */) {
1273
1368
  return this.parsePutStatement();
1274
- } else if (token.type === 20 /* KeywordGet */) {
1369
+ } else if (token.type === 20 /* KeywordGet */ && this.peek(1).type === 111 /* OperatorHash */) {
1275
1370
  return this.parseGetStatement();
1276
1371
  } else if (token.type === 84 /* KeywordInput */) {
1277
1372
  return this.parseInputStatement();
@@ -1291,7 +1386,7 @@ var Parser = class _Parser {
1291
1386
  return this.parseLockStatement();
1292
1387
  } else if (token.type === 100 /* KeywordUnlock */) {
1293
1388
  return this.parseUnlockStatement();
1294
- } else if (token.type === 109 /* KeywordWidth */ && this.peek(1).type !== 120 /* OperatorEquals */) {
1389
+ } else if (token.type === 109 /* KeywordWidth */ && this.peek(1).type === 111 /* OperatorHash */) {
1295
1390
  return this.parseWidthStatement();
1296
1391
  } else if (token.type === 69 /* KeywordClass */ && this.peek(1).type !== 120 /* OperatorEquals */) {
1297
1392
  return this.parseClassDeclaration();
@@ -1304,59 +1399,8 @@ var Parser = class _Parser {
1304
1399
  return { type: "CallStatement", expression: { type: "CallExpression", callee: expr, args: [] } };
1305
1400
  }
1306
1401
  this.throwError(`Parse error: Expected procedure call after 'Call'`);
1307
- } else if (token.type === 0 /* Identifier */ || token.type === 138 /* ForeignName */ || token.type === 129 /* OperatorDot */ || token.type === 1 /* Number */ || _Parser.CONTEXTUAL_KW.has(token.type)) {
1308
- if ((token.type === 0 /* Identifier */ || _Parser.CONTEXTUAL_KW.has(token.type)) && this.pos + 1 < this.tokens.length && this.tokens[this.pos + 1].type === 130 /* OperatorColon */) {
1309
- const labelName = token.value;
1310
- this.advance();
1311
- this.advance();
1312
- return { type: "LabelStatement", label: labelName };
1313
- } else if (token.type === 1 /* Number */) {
1314
- const labelName = token.value;
1315
- this.advance();
1316
- this.match(130 /* OperatorColon */);
1317
- return { type: "LabelStatement", label: labelName };
1318
- }
1319
- const savedPos = this.pos;
1320
- const expr = this.parsePrimary();
1321
- if (this.match(120 /* OperatorEquals */)) {
1322
- return {
1323
- type: "AssignmentStatement",
1324
- left: expr,
1325
- right: this.parseExpression()
1326
- };
1327
- } else {
1328
- if (expr.type === "CallExpression" && this.isBinaryOnlyOperator(this.peek().type)) {
1329
- this.pos = savedPos;
1330
- const callee = this.parsePrimary(
1331
- /* stopBeforeSpacedLParen= */
1332
- true
1333
- );
1334
- const args2 = [this.parseCallArgument()];
1335
- while (this.match(126 /* OperatorComma */)) {
1336
- args2.push(this.parseCallArgument());
1337
- }
1338
- return { type: "CallStatement", expression: { type: "CallExpression", callee, args: args2 } };
1339
- }
1340
- const args = [];
1341
- if (this.peek().type !== 135 /* Newline */ && this.peek().type !== 136 /* EOF */ && this.peek().type !== 9 /* KeywordElse */ && this.peek().type !== 8 /* KeywordElseIf */ && this.peek().type !== 10 /* KeywordEnd */ && this.peek().type !== 5 /* KeywordNext */ && this.peek().type !== 14 /* KeywordLoop */) {
1342
- args.push(this.parseCallArgument());
1343
- while (this.match(126 /* OperatorComma */)) {
1344
- args.push(this.parseCallArgument());
1345
- }
1346
- }
1347
- if (args.length > 0) {
1348
- return { type: "CallStatement", expression: { type: "CallExpression", callee: expr, args } };
1349
- } else if (expr.type === "CallExpression") {
1350
- const callExpr = expr;
1351
- if (callExpr.args.length === 0 && callExpr.callee.type === "Identifier" && !callExpr.callee.foreign) {
1352
- const line = callExpr.loc?.start.line ?? this.peek().line;
1353
- this.throwError(`Parse error: syntax error at line ${line}`);
1354
- }
1355
- return { type: "CallStatement", expression: callExpr };
1356
- } else {
1357
- return { type: "CallStatement", expression: { type: "CallExpression", callee: expr, args: [] } };
1358
- }
1359
- }
1402
+ } else if (token.type === 0 /* Identifier */ || token.type === 138 /* ForeignName */ || token.type === 129 /* OperatorDot */ || token.type === 70 /* KeywordMe */ || token.type === 1 /* Number */ || _Parser.CONTEXTUAL_KW.has(token.type) || _Parser.COMPAT_KW_EXPR.has(token.type)) {
1403
+ return this.parseIdentifierOrCallStatement();
1360
1404
  } else if (token.type === 137 /* Unknown */) {
1361
1405
  this.throwError(`Parse error: Unknown token '${this.tokenDisplay(token.value)}' at line ${token.line}`);
1362
1406
  } else {
@@ -1364,7 +1408,7 @@ var Parser = class _Parser {
1364
1408
  }
1365
1409
  return null;
1366
1410
  }
1367
- parseProcedureDeclaration(scope, isStatic) {
1411
+ parseProcedureDeclaration(scope, isStatic, isClassMember = false) {
1368
1412
  const isFunction = this.peek().type === 18 /* KeywordFunction */;
1369
1413
  const isProperty = this.peek().type === 19 /* KeywordProperty */;
1370
1414
  this.advance();
@@ -1382,6 +1426,9 @@ var Parser = class _Parser {
1382
1426
  if (!this.isIdentifier(idToken) && (idToken.type < 79 /* KeywordBase */ || idToken.type > 110 /* KeywordAddressOf */)) {
1383
1427
  this.throwError(`Parse error at line ${idToken.line}: Expected procedure name (Found ${this.tokenDisplay(idToken.value)})`);
1384
1428
  }
1429
+ if (!isClassMember && _Parser.STATEMENT_KW_RESERVED.has(idToken.type)) {
1430
+ this.throwError(`Compile error at line ${idToken.line}: '${idToken.value}' is a reserved word and cannot be used as a procedure name`);
1431
+ }
1385
1432
  const name = this.makeIdentifier(idToken);
1386
1433
  const parameters = [];
1387
1434
  let paramsEndColumn;
@@ -1544,7 +1591,7 @@ var Parser = class _Parser {
1544
1591
  parseGoToStatement() {
1545
1592
  this.advance();
1546
1593
  const labelToken = this.advance();
1547
- if (labelToken.type !== 0 /* Identifier */ && labelToken.type !== 1 /* Number */) {
1594
+ if (!this.isIdentifier(labelToken) && labelToken.type !== 1 /* Number */) {
1548
1595
  this.throwError(`Parse error: Expected identifier or number after 'GoTo' at line ${labelToken.line}`);
1549
1596
  }
1550
1597
  return { type: "GoToStatement", label: labelToken.value };
@@ -1667,7 +1714,7 @@ var Parser = class _Parser {
1667
1714
  parseClassDeclaration() {
1668
1715
  this.advance();
1669
1716
  const nameToken = this.advance();
1670
- if (nameToken.type !== 0 /* Identifier */) {
1717
+ if (!this.isIdentifier(nameToken)) {
1671
1718
  this.throwError(`Parse error: Expected class name after 'Class' at line ${nameToken.line}`);
1672
1719
  }
1673
1720
  return this.parseClassBody(nameToken.value, true);
@@ -1693,7 +1740,7 @@ var Parser = class _Parser {
1693
1740
  }
1694
1741
  const inner = this.peek();
1695
1742
  if (inner.type === 17 /* KeywordSub */ || inner.type === 18 /* KeywordFunction */ || inner.type === 19 /* KeywordProperty */) {
1696
- const proc = this.parseProcedureDeclaration(scope);
1743
+ const proc = this.parseProcedureDeclaration(scope, void 0, true);
1697
1744
  proc.moduleName = className;
1698
1745
  procedures.push(proc);
1699
1746
  body.push(proc);
@@ -1709,13 +1756,16 @@ var Parser = class _Parser {
1709
1756
  } else if (inner.type === 102 /* KeywordEvent */) {
1710
1757
  const event = this.parseEventDeclaration(scope);
1711
1758
  body.push(event);
1759
+ } else if (inner.type === 31 /* KeywordConst */) {
1760
+ const constDecl = this.parseConstDeclaration();
1761
+ body.push(constDecl);
1712
1762
  } else if (inner.type === 68 /* KeywordStatic */) {
1713
1763
  this.advance();
1714
1764
  const field = this.parseDimStatement(true, true);
1715
1765
  field.scope = scope ?? "public";
1716
1766
  fields.push(field);
1717
1767
  body.push(field);
1718
- } else if (scope !== void 0 && inner.type === 0 /* Identifier */) {
1768
+ } else if (scope !== void 0 && (this.isIdentifier(inner) || inner.type === 104 /* KeywordWithEvents */)) {
1719
1769
  const field = this.parseDimStatement(false, true);
1720
1770
  field.scope = scope;
1721
1771
  fields.push(field);
@@ -2194,7 +2244,8 @@ var Parser = class _Parser {
2194
2244
  } else if (token.type === 0 /* Identifier */ || _Parser.CONTEXTUAL_KW.has(token.type) || _Parser.COMPAT_KW_EXPR.has(token.type)) {
2195
2245
  expr = { type: "Identifier", name: token.value };
2196
2246
  } else if (token.type === 110 /* KeywordAddressOf */) {
2197
- const procName = this.consume(0 /* Identifier */, "Expected procedure name after 'AddressOf'");
2247
+ const procName = this.advance();
2248
+ if (!this.isIdentifier(procName)) this.throwError(`Parse error at line ${procName.line}: Expected procedure name after 'AddressOf'`);
2198
2249
  expr = { type: "AddressOfExpression", procedureName: { type: "Identifier", name: procName.value } };
2199
2250
  } else if (token.type === 44 /* KeywordEmpty */) {
2200
2251
  expr = { type: "Identifier", name: token.value };
@@ -2229,7 +2280,7 @@ var Parser = class _Parser {
2229
2280
  this.throwError(`Parse error: Expected 'Is' after 'TypeOf' at line ${this.peek().line}`);
2230
2281
  }
2231
2282
  const typeToken = this.advance();
2232
- if (typeToken.type !== 0 /* Identifier */ && typeToken.type !== 25 /* KeywordCollection */) {
2283
+ if (!this.isIdentifier(typeToken) && typeToken.type !== 25 /* KeywordCollection */) {
2233
2284
  this.throwError(`Parse error: Expected type name after 'Is' at line ${typeToken.line}`);
2234
2285
  }
2235
2286
  expr = { type: "TypeOfIsExpression", expression: expr, typeName: typeToken.value };
@@ -2318,7 +2369,7 @@ var Parser = class _Parser {
2318
2369
  const labels = [];
2319
2370
  while (true) {
2320
2371
  const labelToken = this.advance();
2321
- if (labelToken.type !== 0 /* Identifier */ && labelToken.type !== 1 /* Number */) {
2372
+ if (!this.isIdentifier(labelToken) && labelToken.type !== 1 /* Number */) {
2322
2373
  this.throwError(`Parse error: Expected label (identifier or number) in On...GoTo/GoSub at line ${labelToken.line}`);
2323
2374
  }
2324
2375
  labels.push(labelToken.value);
@@ -2331,7 +2382,7 @@ var Parser = class _Parser {
2331
2382
  parseGoSubStatement() {
2332
2383
  this.advance();
2333
2384
  const labelToken = this.advance();
2334
- if (labelToken.type !== 0 /* Identifier */ && labelToken.type !== 1 /* Number */) {
2385
+ if (!this.isIdentifier(labelToken) && labelToken.type !== 1 /* Number */) {
2335
2386
  this.throwError(`Parse error: Expected label after GoSub at line ${labelToken.line}`);
2336
2387
  }
2337
2388
  return { type: "GoSubStatement", label: labelToken.value };
@@ -2361,7 +2412,8 @@ var Parser = class _Parser {
2361
2412
  }
2362
2413
  parseEventDeclaration(scope) {
2363
2414
  this.advance();
2364
- const idToken = this.consume(0 /* Identifier */, "Expected identifier after 'Event'");
2415
+ const idToken = this.advance();
2416
+ if (!this.isIdentifier(idToken) && !this.isNameToken(idToken)) this.throwError(`Expected identifier after 'Event' at line ${idToken.line}`);
2365
2417
  const name = { type: "Identifier", name: idToken.value };
2366
2418
  const parameters = [];
2367
2419
  if (this.match(127 /* OperatorLParen */)) {
@@ -2377,7 +2429,8 @@ var Parser = class _Parser {
2377
2429
  }
2378
2430
  parseRaiseEventStatement() {
2379
2431
  this.advance();
2380
- const idToken = this.consume(0 /* Identifier */, "Expected identifier after 'RaiseEvent'");
2432
+ const idToken = this.advance();
2433
+ if (!this.isIdentifier(idToken) && !this.isNameToken(idToken)) this.throwError(`Expected identifier after 'RaiseEvent' at line ${idToken.line}`);
2381
2434
  const eventName = { type: "Identifier", name: idToken.value };
2382
2435
  const args = [];
2383
2436
  if (this.match(127 /* OperatorLParen */)) {
@@ -2393,7 +2446,8 @@ var Parser = class _Parser {
2393
2446
  }
2394
2447
  parseImplementsDirective() {
2395
2448
  this.advance();
2396
- const idToken = this.consume(0 /* Identifier */, "Expected interface name after 'Implements'");
2449
+ const idToken = this.advance();
2450
+ if (!this.isIdentifier(idToken)) this.throwError(`Parse error at line ${idToken.line}: Expected interface name after 'Implements'`);
2397
2451
  return { type: "ImplementsDirective", interfaceName: idToken.value };
2398
2452
  }
2399
2453
  parseAppActivateStatement() {
@@ -2705,6 +2759,11 @@ function evaluateCCExpr(expr, resolve) {
2705
2759
  // ../../test-libs/vba-analyzer.ts
2706
2760
  var fs = __toESM(require("fs"), 1);
2707
2761
  var path = __toESM(require("path"), 1);
2762
+
2763
+ // ../../test-libs/version.ts
2764
+ var VERSION = "0.1.1-alpha.1";
2765
+
2766
+ // ../../test-libs/vba-analyzer.ts
2708
2767
  function isStatementArray(v) {
2709
2768
  return Array.isArray(v);
2710
2769
  }
@@ -4208,7 +4267,8 @@ function analyzeFile(filePath) {
4208
4267
  definedTypes: /* @__PURE__ */ new Set(),
4209
4268
  callsByProc: /* @__PURE__ */ new Map(),
4210
4269
  xlVbConstantRefs: /* @__PURE__ */ new Set(),
4211
- commentedCodeBlocks: detectCommentedCode(src, filePath)
4270
+ commentedCodeBlocks: detectCommentedCode(src, filePath),
4271
+ withEventsVars: /* @__PURE__ */ new Set()
4212
4272
  };
4213
4273
  }
4214
4274
  if (ast.diagnostics && ast.diagnostics.length > 0) {
@@ -4287,13 +4347,22 @@ function analyzeFile(filePath) {
4287
4347
  const xlVbConstantRefs = collectExcelConstantRefs(ast);
4288
4348
  const gotoGraphs = procs.map((proc) => analyzeGotoInProc(proc));
4289
4349
  const loopAnalyses = procs.map((proc, i) => analyzeLoopsInProc(proc, gotoGraphs[i]));
4350
+ const withEventsVars = /* @__PURE__ */ new Set();
4351
+ for (const s of ast.body) {
4352
+ if (s.type === "VariableDeclaration") {
4353
+ for (const decl of s.declarations ?? []) {
4354
+ if (decl.isWithEvents) withEventsVars.add((decl.name?.name ?? "").toLowerCase());
4355
+ }
4356
+ }
4357
+ }
4290
4358
  return {
4291
4359
  report: { filePath, totalLines: lines.length, procedureCount: procedures.length, procedures, prefixClusters, warnings, gotoGraphs, loopAnalyses },
4292
4360
  definedProcs,
4293
4361
  definedTypes,
4294
4362
  callsByProc,
4295
4363
  xlVbConstantRefs,
4296
- commentedCodeBlocks: detectCommentedCode(src, filePath)
4364
+ commentedCodeBlocks: detectCommentedCode(src, filePath),
4365
+ withEventsVars
4297
4366
  };
4298
4367
  }
4299
4368
  function buildWorkspaceReport(analyses) {
@@ -4325,6 +4394,10 @@ function buildWorkspaceReport(analyses) {
4325
4394
  for (const a of analyses) {
4326
4395
  for (const p of a.report.procedures) {
4327
4396
  if (p.referenceCount === 0) {
4397
+ const nameLower = p.name.toLowerCase();
4398
+ const sep = nameLower.indexOf("_");
4399
+ const isEventHandler = nameLower === "class_initialize" || nameLower === "class_terminate" || sep > 0 && a.withEventsVars.has(nameLower.slice(0, sep));
4400
+ if (isEventHandler) continue;
4328
4401
  if (p.scope === "private") {
4329
4402
  deadCodeCandidates.push({
4330
4403
  file: a.report.filePath,
@@ -4989,10 +5062,13 @@ function buildDiffJson(baseline, current) {
4989
5062
  }
4990
5063
  };
4991
5064
  }
4992
- function main() {
4993
- const args = process.argv.slice(2);
5065
+ function main(args) {
5066
+ if (args.includes("--version") || args.includes("-v")) {
5067
+ console.log(VERSION);
5068
+ process.exit(0);
5069
+ }
4994
5070
  if (args.length === 0 || args.includes("--help")) {
4995
- console.log("Usage: npx tsx test-libs/vba-analyzer.ts <file-or-dir> [options]");
5071
+ console.log("Usage: vba-runner analyze <file-or-dir> [options]");
4996
5072
  console.log("");
4997
5073
  console.log(" --json JSON \u5F62\u5F0F\u3067\u51FA\u529B\uFF08\u30D7\u30ED\u30B0\u30E9\u30E0\u9023\u643A\u7528\uFF09");
4998
5074
  console.log(" --diff <baseline> baseline JSON \u3068\u306E\u5DEE\u5206\u3092\u8868\u793A\uFF08--json \u3068\u4F75\u7528\u53EF\uFF09");
@@ -5002,6 +5078,7 @@ function main() {
5002
5078
  console.log(" --goto-graph GoTo \u6587\u306E\u5236\u5FA1\u30D5\u30ED\u30FC\u30B0\u30E9\u30D5\u3068\u30EA\u30D5\u30A1\u30AF\u30BF\u30EA\u30F3\u30B0\u63D0\u6848\u3092\u8868\u793A");
5003
5079
  console.log(" --cross-iter \u30AF\u30ED\u30B9\u30A4\u30C6\u30EC\u30FC\u30B7\u30E7\u30F3\u5909\u6570\uFF08ByRef \u5FC5\u8981\u5909\u6570\uFF09\u306E\u4E00\u89A7\u3092\u8868\u793A");
5004
5080
  console.log(" --gen-test-dir <dir> \u30C6\u30B9\u30C8\u7528\u30BD\u30FC\u30B9\u3092\u6307\u5B9A\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u751F\u6210\uFF08const.ts \u7B49\uFF09");
5081
+ console.log(" --version \u30D0\u30FC\u30B8\u30E7\u30F3\u3092\u8868\u793A");
5005
5082
  process.exit(args.length === 0 ? 1 : 0);
5006
5083
  }
5007
5084
  const wantJson = args.includes("--json");
@@ -5076,7 +5153,7 @@ function main() {
5076
5153
  );
5077
5154
  if (hasParseErrors) process.exit(1);
5078
5155
  }
5079
- if (process.argv[1]?.includes("vba-analyzer")) main();
5156
+ if (process.argv[1]?.includes("vba-analyzer")) main(process.argv.slice(2));
5080
5157
  function analyzeWorkspaceForMcpFromFiles(files) {
5081
5158
  const analyses = files.map((f) => analyzeFile(f));
5082
5159
  return buildWorkspaceReport(analyses);
@@ -5100,6 +5177,7 @@ function formatWorkspaceReportForMcp(w) {
5100
5177
  formatWorkspaceOutlineForMcp,
5101
5178
  formatWorkspaceReportForMcp,
5102
5179
  formatWorkspaceSummary,
5180
+ main,
5103
5181
  paramName,
5104
5182
  vbaTypeToTs
5105
5183
  });
@@ -5,6 +5,10 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
8
12
  var __copyProps = (to, from, except, desc) => {
9
13
  if (from && typeof from === "object" || typeof from === "function") {
10
14
  for (let key of __getOwnPropNames(from))
@@ -21,8 +25,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
25
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
26
  mod
23
27
  ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
24
29
 
25
30
  // ../../test-libs/vba-formatter.ts
31
+ var vba_formatter_exports = {};
32
+ __export(vba_formatter_exports, {
33
+ main: () => main
34
+ });
35
+ module.exports = __toCommonJS(vba_formatter_exports);
26
36
  var fs = __toESM(require("fs"), 1);
27
37
  var path = __toESM(require("path"), 1);
28
38
 
@@ -916,6 +926,9 @@ function applyEdits(source, edits) {
916
926
  return lines.join("\n");
917
927
  }
918
928
 
929
+ // ../../test-libs/version.ts
930
+ var VERSION = "0.1.1-alpha.1";
931
+
919
932
  // ../../test-libs/vba-formatter.ts
920
933
  var VBA_EXTENSIONS = /\.(bas|cls|frm)$/i;
921
934
  function collectFiles(target) {
@@ -950,21 +963,25 @@ function buildDiff(original, formatted, filePath) {
950
963
  }
951
964
  return hasDiff ? diffLines.join("\n") : "";
952
965
  }
953
- function main() {
954
- const args = process.argv.slice(2);
966
+ function main(args) {
967
+ if (args.includes("--version") || args.includes("-v")) {
968
+ console.log(VERSION);
969
+ process.exit(0);
970
+ }
955
971
  if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
956
- console.log(`Usage: vba-formatter <file-or-dir> [--check | --write] [options]
972
+ console.log(`Usage: vba-runner format <file-or-dir> [--check | --write] [options]
957
973
 
958
974
  Options:
959
975
  --check \u5DEE\u5206\u304C\u3042\u308C\u3070\u8868\u793A\u3057\u3066 exit code 1 \u3067\u7D42\u4E86
960
976
  --write \u30D5\u30A1\u30A4\u30EB\u3092\u4E0A\u66F8\u304D\uFF08\u6307\u5B9A\u3057\u306A\u3044\u5834\u5408\u306F stdout \u306B\u51FA\u529B\uFF09
961
977
  --indent-size=N \u30A4\u30F3\u30C7\u30F3\u30C8\u5E45\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8: 4\uFF09
962
978
  --no-keyword-case \u30AD\u30FC\u30EF\u30FC\u30C9\u306E\u5927\u6587\u5B57\u5316\u3092\u7121\u52B9\u5316
979
+ --version \u30D0\u30FC\u30B8\u30E7\u30F3\u3092\u8868\u793A
963
980
 
964
981
  Example:
965
- npx tsx test-libs/vba-formatter.ts src/vba/Main.bas
966
- npx tsx test-libs/vba-formatter.ts src/vba/ --check
967
- npx tsx test-libs/vba-formatter.ts src/vba/Main.bas --write --indent-size=2`);
982
+ vba-runner format src/vba/Main.bas
983
+ vba-runner format src/vba/ --check
984
+ vba-runner format src/vba/Main.bas --write --indent-size=2`);
968
985
  process.exit(0);
969
986
  }
970
987
  const target = args.find((a) => !a.startsWith("-"));
@@ -1015,4 +1032,8 @@ Example:
1015
1032
  process.exit(1);
1016
1033
  }
1017
1034
  }
1018
- main();
1035
+ if (process.argv[1]?.includes("vba-formatter")) main(process.argv.slice(2));
1036
+ // Annotate the CommonJS export names for ESM import in node:
1037
+ 0 && (module.exports = {
1038
+ main
1039
+ });