Skip to content

refactor(vba): replace the positional extractor parameters with an options object - #271

Merged
ardelperal merged 1 commit into
mainfrom
refactor/issue-243-extraction-options
Sep 1, 2026
Merged

ardelperal merged 1 commit into
mainfrom
refactor/issue-243-extraction-options

Conversation

@ardelperal

Copy link
Copy Markdown
Owner

Closes #243

What

The VBA extraction knobs travelled as positional parameters through five files:

project-config.ts      loadVbaConfig()
  -> extraction/index.ts       reads vbaConfig, picks maxRaiseFanout
    -> extraction/parse-pool.ts    ParseTask fields
      -> extraction/parse-worker.ts   message shape
        -> extraction/tree-sitter.ts  extractFromSource(..., vbaTargets, maxRaiseFanout, ...)
          -> new VbaExtractor(filePath, source, vbaTargets, maxRaiseFanout)

Adding vba.sqlWrappers as the next knob meant touching all five again. This collapses the chain onto one object.

  • New leaf module src/extraction/vba/options.ts exporting VbaExtractionOptions with targets, maxRaiseFanout, sqlWrappers. It imports nothing, so the config layer, the pool, the worker and the extractor can all depend on it without a cycle.
  • VbaExtractor takes options: VbaExtractionOptions = {} as its 3rd parameter. The positional 3rd/4th form still works through a @deprecated overload for one release; every in-repo call site moved to the object form in this PR (including __tests__/extraction-vba-event-fanout.test.ts).
  • extractFromSource gains a trailing vbaOptions?: VbaExtractionOptions. The legacy vbaTargets / maxRaiseFanout positionals merge into it per field, object wins on conflict.
  • ParseTask and the worker message carry vbaOptions as ONE field, replacing the two separate ones.
  • sqlWrappers is declared and threaded end to end now; the SQL-wrapper task is its consumer.

Overload disambiguation

The implementation signature receives either an options object or a legacy Record<string, boolean> targets map as its 3rd argument. The runtime discriminator is the value types, not the key names: a targets map has only boolean values; VbaExtractionOptions never does (targets is an object, maxRaiseFanout a number, sqlWrappers an array). So a project that happens to name a #Const targets is still read correctly. {} is vacuously "all booleans" and is read as an empty legacy targets map — which is behaviourally identical to empty options, because an empty targets map and undefined both mean "no project overrides" to preprocessConditionalCompilation, and an absent maxRaiseFanout falls back to DEFAULT_MAX_RAISE_FANOUT either way.

Worker-boundary constraint

The parse-pool -> parse-worker hop is structuredClone-based, so VbaExtractionOptions is plain data: no functions, no RegExp, no Map, no class instances. The module docstring states the rule for whoever adds field four; a compiled wrapper matcher belongs on the extractor context, built inside the extractor from the plain strings in sqlWrappers.

No CHANGELOG entry

This is a pure refactor with no user-visible change: no new config key is read, no output differs, no CLI surface moves. Per the repo's CHANGELOG discipline there is deliberately no entry.

Tests

New __tests__/extraction-vba-options.test.ts — 13 tests, real files, real extractor, a real worker_threads.Worker, no mocking:

  • object form and legacy positional form produce deep-equal ExtractionResult (ignoring only the wall-clock durationMs and Node.updatedAt), for both VbaExtractor and extractFromSource
  • targets is verified to actually do something in the same assertion (#If DEV gates a DevOnly Sub), so an equal-and-both-wrong result cannot pass
  • {}, no arguments, and explicit-undefined positionals are pinned as the same run
  • object-wins-per-field on conflict, with a sanity assertion that the positional value alone really would not have gated
  • maxRaiseFanout regression guard for fix(vba): fanout cap for generic event names (Change/Click/AfterUpdate/etc.) to suppress graph noise #152: over the threshold drops every raises-event edge and flags highFanout/raiseCount; at the threshold every edge survives (strict >); object and positional forms gate identically
  • structuredClone(options) round-trips unchanged
  • a real worker round-trip: a worker_threads.Worker speaking the pool's {load-grammars -> grammars-loaded} / {parse -> parse-result} protocol echoes the vbaOptions it received back across the boundary. Both hops are genuine structuredClones, so this fails the moment a non-cloneable value is added to the interface. An omitted vbaOptions is pinned as arriving undefined, not as a mangled object.

Verification

npx tsc --noEmit                                            clean
pnpm run build                                              clean
npx vitest run __tests__/extraction-vba-options.test.ts     13 passed
pnpm exec vitest run vba extraction-sql-query sql-query-discovery
                                                            45 files passed, 702 passed / 1 skipped

45 files is the 44-file baseline on main plus the one file this PR adds. Nothing else moved.

__tests__/extraction.test.ts has 2 pre-existing failures in the "Git Submodules" / "Nested gitlink repos" blocks. They reproduce identically on a clean main checkout and are unrelated to this change.

Zero behaviour change: identical probe output

The committed probe was run over the same two real Access source trees from a clean main checkout and from this branch:

npm run probe:vba -- "C:/00repos/codigo/00_EXPEDIENTES/src" "C:/00repos/codigo/00_GESTION_RIESGOS/src"

The two reports are byte-identical — diff is empty and the SHA-256 of both files is 6e8084ded2a4901fb5efaceffe7ac5ff4e761d9beb7a3115fbb52b8f39832f86.

field main this branch
declared procedures 3,840 3,840
stub function nodes 6,132 6,132
class nodes 1,578 1,578
calls edges 8,322 8,322
contains edges 6,248 6,248
references edges 4,969 4,969
event-handler edges 695 695
unresolved calls 7,679 7,679

Every other field in the report (files by extension, all 15 node kinds, edges by synthesizer, the top-30 stub targets, SQL tables, forms, errors) is identical too — that is what byte-identical means here.

…tions object

The VBA extraction knobs travelled as positional parameters through five
files (project-config -> extraction/index -> parse-pool -> parse-worker ->
tree-sitter -> vba-extractor). Adding one knob meant editing every
signature and every call site.

Introduce the leaf module `src/extraction/vba/options.ts` exporting
`VbaExtractionOptions` (`targets`, `maxRaiseFanout`, `sqlWrappers`) and
thread that ONE object end to end:

- `VbaExtractor` takes `options` as its 3rd parameter. The positional
  3rd/4th form is kept working through a `@deprecated` overload for one
  release; every in-repo call site moves to the object form.
- `extractFromSource` gains a trailing `vbaOptions`; the legacy
  positionals merge into it per field, with the object winning.
- `ParseTask` and the worker message carry `vbaOptions` as one field.

`sqlWrappers` is declared and threaded now; the SQL-wrapper task consumes
it. The object stays plain structured-cloneable data because it crosses
the `structuredClone`-based parse-worker boundary — no functions, no
RegExp, no Map. A compiled matcher belongs on the extractor context.

Pure refactor: the VBA coverage probe over 00_EXPEDIENTES and
00_GESTION_RIESGOS is byte-identical to main.
@ardelperal
ardelperal merged commit a0345ae into main Sep 1, 2026
3 checks passed
@ardelperal
ardelperal deleted the refactor/issue-243-extraction-options branch September 1, 2026 18:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor(vba): replace seven positional extractor parameters with an options object

1 participant