Contributing to Gherkin PowerTools
Contributions to Gherkin PowerTools are welcome. This document explains the architecture, local project setup, and submission process.
๐๏ธ Architecture Overview
The extension is written in TypeScript and uses the native VS Code Extension API. It is built with performance and maintainability in mind.
Here is a breakdown of the core modules located in the src/ directory:
extension.ts: The entry point and minimal composition root. Bundled via Esbuild for fast activation. Delegates capability registration to specialized submodules insrc/activation/.src/activation/: Contains modular activation logic forcommands.ts,migration.ts,contextService.ts, andwalkthrough.ts.eventBus.ts: The centralized publish/subscribe Workspace Event Bus. It handles file watchers and decouples feature modules from VS Code workspace events.formatter.ts: The core AST-based formatter. It handles indentation, table alignment, auto-casing, and tag wrapping based on@cucumber/gherkinparses.highlighter.ts: Implements custom semantic syntax highlighting via VS Code'screateTextEditorDecorationTypeAPI.linter.ts: Uses the official@cucumber/gherkinAST parser to perform real-time syntax checking. Generatesvscode.Diagnosticwarnings to underline mistakes in the editor.definition.ts: The Go-To-Definition provider. Accessescache.tsfor instant lookups.outline.ts: Constructs the hierarchical tree ofFeature > Rule > Scenariofor the VS Code Outline panel.statistics.ts: Generates the interactive HTML Webview dashboard by parsing workspace files to count BDD metrics.codeAction.ts: Generates quick fixes (๐ก) for undefined steps or syntax typos.completion.ts: Smart IntelliSense autocompletion parsing regex into Snippets.cache.ts: Asynchronous caching engine that non-blockingly indexes the workspace viavscode.workspace.findFiles.logger.ts: Native VS Code Output Channel for tracing.hover.ts: Provides hover information such as function signatures, docstrings, and tag blast radius.parser.ts: Handles AST parsing and caching of Gherkin documents.dialect.ts: Provides i18n support by matching localized Gherkin keywords.discovery.ts: Centralized service for Behave step-file discovery, configuration normalization, and reactive file watchers.diagnostics.ts: Diagnostic engine (Gherkin: Diagnose Workspace) collecting system metrics, discovery stats, and redacting paths for safe troubleshooting.
Architecture Rules
To maintain compatibility and prevent side effects in users' workspaces, developers must adhere to the following strict boundaries:
- No Global Configuration Overrides: The extension must never specify overrides for native VS Code settings (e.g., testing.*, editor.*) inside the configurationDefaults block in package.json. Doing so silently breaks other extensions installed by the user. If an integration problem exists, it must be solved inside our own controllers, not by altering the global user workspace.
- Test Controller Coexistence: The extension must safely coexist with other extensions that provide their own Test Controllers and Profiles (like Python pytest or Coverage). Our Test Controller should not assume it is the only one in the workspace. Any E2E UI test must instantiate Mock controllers with unique IDs to avoid ID collisions with the real extension background processes.
- Transactional Graph State: The WorkspaceGraph coordinates the semantic index. Any mutation of the graph state must occur inside the graph.executeTransaction() wrapper to guarantee atomic commits and failure isolation.
Services requiring graph reads must query the immutable graph.currentGeneration object. Tests needing to inject state outside of the transaction queue should exclusively use the test-only helper graph.setNodeForTest().
- Lifecycle and Disposal Rules: When implementing global singletons or event-driven services (e.g., MetricsLogger), event listeners (such as onDidChangeConfiguration) must not be hidden inside class constructors. Instead, they must be explicitly bound and tracked via the ExtensionContext.subscriptions in the activation phase to prevent memory leaks.
- Testing Requirements: Any global state used by singletons must be explicitly cleared using dedicated reset() methods during the test teardown() phase to prevent cross-test pollution and state leaking.
Contributors Deep-Dive: Implementing a new Anti-Pattern Rule
When contributing a new rule to the AntiPatternEngine, you must implement the AntiPatternRule<T> interface located in src/antiPatternEngine.ts.
1. Rule Classification: You must categorize your rule appropriately (Correctness, Reliability, Maintainability, Style) via RuleMetadata. Do not treat subjective heuristics (Maintainability) as objective Correctness errors.
2. Object Configuration: If your rule relies on numeric thresholds (e.g., maximum steps), you must declare a generic configuration interface <T> (e.g., OversizedScenarioParams) and provide sensible defaults in defaultParams. The engine will automatically pass the resolved configuration object to your analyze() method.
3. Immutability: Analyze the WorkspaceGraph strictly in a read-only manner. Do not mutate nodes during analysis.
Contributors Deep-Dive: Implementing a new LanguageService Provider
When adding a new Language Service provider (e.g. HoverProvider, CodeLensProvider, or CompletionItemProvider), you must adhere to the following strict architectural constraints to ensure it performs efficiently and without cross-platform bugs:
- Never perform synchronous disk I/O: Language providers are called hundreds of times per second. Query the
WorkspaceGraphorSymbolCachesynchronously, as they represent the in-memory state. - Always route through
ResourceIdentity.getCanonicalUriString(): VS Code URI representations vary by platform (file:///Users/C...vsfile:///users/C...). When you extract a URI from avscode.TextDocumentorvscode.Uriprovided by the extension host to look up a node in the graph, you MUST convert it using theResourceIdentitycanonifier before executing.get(...)or.has(...)on internal Maps. Failing to do so will result in providers breaking silently on macOS and Windows (case-insensitive filesystems).
๐ ๏ธ Local Setup
- Prerequisites: Ensure you have Node.js (v22+) and npm installed.
- Clone the repository:
- Install dependencies:
- Compile the TypeScript code:
- Build the CLI executable:
- Run the Extension:
- Press
F5in VS Code to open a new "Extension Development Host" window. - Any changes you make to the code can be tested by reloading the Development Host (
Cmd + R/Ctrl + R).
To test the CLI locally after building:
๐งช Testing
The official @vscode/test-electron framework coupled with Mocha is used to run tests. Tests are split into two categories to maximize efficiency and reliability:
Configuration Drift Check
To verify that all configuration settings in package.json, gherkin-powertools.schema.json, src/configuration.ts, README.md, and documentation are 100% synchronized:
CLI Tests
To run the integration tests specifically for the Command Line Interface:
Unit and Architecture Tests
To run ultra-fast unit tests that validate the AST processor and algorithms, as well as the Architecture Validation Test Suite (which ensures all commands are registered, watchers are disposed, and bootstrap completes successfully):
To run the unit and architecture tests and generate an LCOV coverage report:
End-to-End (E2E) UI Tests
To run native UI integration tests that launch a real VS Code instance and test features like formatting, outline generation, and linting directly via the VS Code Extension APIs:
Important: Always ensure that all tests pass before submitting a Pull Request. If you are adding a new feature, please add a corresponding test case in the
src/test/directory.
๐ค CI/CD Pipeline
The CI/CD pipeline ensures that all code meets our quality and security standards.
Supply-Chain Immutability
To prevent supply-chain attacks, all third-party GitHub Actions must be pinned to a full 40-character commit SHA, not a mutable tag or branch (e.g., @v2 or @main).
This guarantees that the exact execution logic cannot be silently altered by an upstream provider.
- You must leave a readable comment next to the SHA indicating the intended version/tag (e.g., # v2.1.0).
- If you add or modify a GitHub workflow, you must run npm run check:config locally. This executes our static workflow policy checker (scripts/test-workflow-policy.js) which will fail the build if any unpinned, mutable action references are found.
Coverage reporting and other QA gates are handled by these rigorously pinned actions.
๐ฆ Packaging
To verify that the built VSIX package contains only expected files, doesn't leak secrets, and doesn't exceed maximum size limitations:
To create a local .vsix file for distribution or local testing:
This will generate a vscode-gherkin-powertools-x.x.x.vsix file in the root directory.
๐ค Submitting a Pull Request
โน๏ธ NOTE: If you are planning a large feature or significant architectural change, please open an Issue or Discussion first to align with the project maintainers before writing code. Please use our structured Issue Forms to report bugs, performance problems, or feature requests before starting.
- Fork the repository.
- Create a new branch for your feature or bug fix:
git checkout -b feature/my-new-feature - Commit your changes:
git commit -m 'Add some feature' - Push to the branch:
git push origin feature/my-new-feature - Open a Pull Request against the
mainbranch.
Code reviews will be conducted on all submissions.
๐ Release Preparation Process
To ensure high-quality, secure releases, we use an explicit two-step release architecture that decouples unprivileged compilation from privileged publication.
- Prepare the Release locally:
- Update the version in
package.json(e.g. from1.8.4to1.8.5). - Run
npm installto updatepackage-lock.json. - Add a new section in
CHANGELOG.mdwith the exact header## [1.8.5]. -
Commit and push these changes to
main. (Note: pushing to main no longer triggers an automatic release). -
Trigger the Release Workflow:
- Go to the Actions tab in the GitHub repository.
- Select the ๐ฆ Create Release workflow on the left.
- Click Run workflow.
- By default, Dry Run is checked. You can run this first to safely validate the build, tests, and signatures without publishing.
-
To officially publish, uncheck "Dry Run" and run the workflow.
-
What happens automatically:
- The unprivileged
build-and-validatejob compiles the extension, runs tests, creates the.vsix, extracts your changelog notes, and generates cryptographic provenance. -
The privileged
publishjob verifies the provenance and creates the Git Tag, GitHub Release, and uploads the verified asset. -
Enhance Release Notes (Optional):
- Go to the GitHub Releases page and edit the generated release to add any visual demos, screenshots, or additional narrative using
.github/RELEASE_TEMPLATE.md.