Conversation
…trol feat: add file size validation in FileSystemService test: add contract validation tests for Barrel types and enums chore: update ESLint rules for TypeScript annotations
…el file generation
…te logic and fix critical bug
…r concurrency control - Added unit tests for array utilities, including `isEmptyArray`. - Implemented comprehensive tests for custom assertion utilities. - Created tests for error handling utilities, including `getErrorMessage` and `formatErrorForLog`. - Developed tests for ESLint plugin helpers and string utilities. - Introduced a Semaphore class to manage concurrency with a limit on concurrent operations. - Added tests for the Semaphore class to ensure correct behavior in queuing and releasing permits. - Updated string utility functions with improved sorting logic and added tests for various scenarios. - Added type definitions for Logger and OutputChannel interfaces.
… and update tests for consistency
…access types; add hardening plan for test coverage
…actor extension tests for improved clarity
… inclusion, and update README logo
- Bump version in package.json from 1.1.0 to 1.1.1. - Enhance BarrelContentBuilder to support TypeScript 4.5+ mixed export syntax, combining value and type exports in a single statement. - Refactor export patterns to handle comments and whitespace variations in export statements. - Update tests to validate new export handling, including mixed exports and comments. - Remove deprecated test files and clean up unused imports in various modules. - Improve logging module exports for better clarity and organization.
…g logging in content sanitizer
…ernals configuration for ts-morph
There was a problem hiding this comment.
Pull request overview
This PR refactors and hardens barrel generation (deduplication, multiline export handling, caching + concurrency), reorganizes the test suite around Node’s node:test, and tightens VSIX packaging via an allowlist-based .vscodeignore. It also updates docs/changelog and bumps the extension version to 1.1.1.
Changes:
- Add export-pattern parsing + content sanitization + export caching to prevent duplicate exports and preserve direct
index.tsdeclarations during updates. - Introduce concurrency helpers (semaphore + concurrent processing) and serialize extension command execution.
- Restructure tests under
src/test/{unit,integration}, update test runner globs/flags, and refresh documentation/packaging metadata.
Reviewed changes
Copilot reviewed 63 out of 66 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
webpack.config.cjs |
Adjust webpack externals to keep ts-morph (and subpaths) out of the bundle. |
src/vscode.ts |
Replace prior test-facing exports with a pure re-export of the VS Code API for easier module mocking. |
src/extension.ts |
Switch to ./vscode.js indirection and add a command queue to serialize barrel operations. |
src/index.ts |
Minor formatting cleanup (remove stray blank line). |
src/utils/string.ts |
Adjust sortAlphabetically behavior to avoid passing undefined locale/options unnecessarily. |
src/utils/semaphore.ts |
Add Semaphore + processConcurrently concurrency utilities. |
src/utils/index.ts |
Export the new semaphore utilities. |
src/types/logger.ts |
Add shared logger type contracts for runtime + test doubles. |
src/types/index.ts |
Rework type barrel exports and include new logger types. |
src/logging/index.ts |
Consolidate type/value exports from the logging barrel. |
src/core/index.ts |
Minor formatting cleanup (remove stray blank line). |
src/core/io/index.ts |
Minor formatting cleanup (remove stray blank line). |
src/core/io/file-system.service.ts |
Expand ignored directories, make matching case-insensitive, add file-size guard + getFileStats(). |
src/core/barrel/index.ts |
Export new barrel helpers (sanitizer, cache, export-pattern utilities). |
src/core/barrel/export-patterns.ts |
Add robust regex-based parsing helpers for export-path extraction + normalization/detection. |
src/core/barrel/content-sanitizer.ts |
Preserve direct declarations while stripping duplicate/parent re-exports (incl. multiline exports). |
src/core/barrel/export-cache.ts |
Cache parsed exports by mtime with bounded size + eviction. |
src/core/barrel/barrel-file.generator.ts |
Integrate sanitizer + export cache + concurrent processing; add recursion depth guard. |
src/core/barrel/barrel-content.builder.ts |
Emit TS 4.5+ mixed export syntax combining value+type exports in a single statement. |
src/test/testTypes.ts |
Inline minimal VS Code types for tests; re-export logger types from src/types. |
src/test/testHarness.ts |
Remove Jest-compat shim (file deleted). |
src/test/runTest.ts |
Point VS Code test runner entry to ./integration/index.js. |
src/test/integration/index.ts |
Add stable placeholder entry point for VS Code integration tests. |
src/test/integration/core/parser/export.parser.integration.test.ts |
Update integration test paths + add recursive test-file discovery. |
src/test/unit/extension.test.ts |
Update mocking to target ../../vscode.js; update logger stub naming and expectations. |
src/test/unit/logging/output-channel.logger.test.ts |
Fix imports due to test folder restructure. |
src/test/unit/utils/*.test.ts |
Update imports to match new folder layout; add new semaphore tests. |
src/test/unit/utils/string.smoke.test.ts |
Add smoke coverage for sortAlphabetically. |
src/test/unit/core/parser/*.test.ts |
Update imports to match new folder layout. |
src/test/unit/core/io/file-system.service.test.ts |
Drop Jest shim usage; add coverage for new file-size guard behavior. |
src/test/unit/core/barrel/*.test.ts |
Add extensive new tests for export-pattern parsing, sanitizer behavior, cache behavior, and generator idempotency/dedup. |
src/test/unit/types/contracts.test.ts |
Add contract tests validating enums/constants/type expectations. |
src/test/unit/core/rules/no-instanceof-error-autofix.test.ts |
Replace prior RuleTester-based tests with lighter metadata/helpers checks (new file). |
src/core/rules/no-instanceof-error-autofix.test.ts |
Remove old RuleTester-based ESLint rule tests (file deleted). |
src/core/barrel/barrel-file.generator.test.ts |
Remove old test file under src/core/** (moved under src/test/unit/**). |
scripts/run-tests.cjs |
Update test discovery globs and enable --experimental-test-module-mocks. |
scripts/eslint-plugin-local.mjs |
Add new local ESLint rules (no-parent-reexport-from-index, no-index-access-types). |
scripts/ast-enums.cjs |
Add new AST node type constants used by the local ESLint rules. |
eslint.config.mjs |
Adjust restricted syntax rules; add new local rules; add no-restricted-imports to discourage direct fs imports. |
.vscodeignore |
Switch to an allowlist-only VSIX packaging strategy. |
.markdownlint.yml |
Disable duplicate heading rule (MD024) for changelog patterns. |
package.json |
Bump version to 1.1.1, refine keywords, remove Jest-related deps, adjust coverage exclude patterns. |
package-lock.json |
Lockfile updates to match dependency changes + version bump. |
.gitignore |
Ignore .depcheck.json and webpack stats artifacts. |
README.md |
Rewrite/refresh docs (features, commands, packaging allowlist, updated coverage badge). |
CONTRIBUTING.md |
Update contributor guidance to match new test/lint/tooling reality. |
AGENTS.md |
Document CI/test/packaging conventions and the VSIX allowlist. |
ARCHITECTURE.md |
Rewrite architecture doc to match current module structure + execution flow. |
CHANGELOG.md |
Add detailed entries for 1.1.1 and 1.1.0. |
hardening.md |
Add/expand hardening plan and test coverage roadmap. |
IDEAS.md |
Add future enhancements backlog doc. |
badges/coverage.svg |
Update coverage badge asset. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Ignore everything by default | ||
| ** | ||
|
|
||
| # Include only these files | ||
| !LICENSE | ||
| !package.json | ||
| !README.md | ||
| !dist/extension.js | ||
| !public/img/barrel-roll-icon.png |
There was a problem hiding this comment.
.vscodeignore now allowlists only dist/extension.js, but webpack.config.cjs still uses devtool: 'source-map', which typically emits dist/extension.js.map and leaves a //# sourceMappingURL=... reference in extension.js. Publishing without the .map can cause noisy “failed to load source map” warnings in devtools. Either include the .map in the allowlist, or switch webpack to hidden-source-map / nosources-source-map / devtool: false for production VSIX output.
| * Creates a new semaphore with the specified number of permits. | ||
| * @param permits The number of permits to initialize the semaphore with. | ||
| */ | ||
| constructor(private permits: number) {} | ||
|
|
||
| /** | ||
| * Acquires a permit from the semaphore. | ||
| * If no permits are available, the promise will wait until one is released. | ||
| * @returns Promise that resolves when a permit is acquired. | ||
| */ | ||
| async acquire(): Promise<void> { | ||
| if (this.permits > 0) { | ||
| this.permits--; | ||
| return; | ||
| } | ||
|
|
||
| return new Promise((resolve) => { | ||
| this.waiting.push(resolve); | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Releases a permit back to the semaphore. | ||
| * If there are waiting callers, the first one in the queue will be resolved. | ||
| */ | ||
| release(): void { | ||
| this.permits++; | ||
| if (this.waiting.length === 0) { | ||
| return; | ||
| } | ||
|
|
||
| const resolve = this.waiting.shift()!; | ||
| this.permits--; | ||
| resolve(); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the current number of available permits. | ||
| * @returns The number of available permits. | ||
| */ | ||
| get availablePermits(): number { | ||
| return this.permits; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the number of callers waiting for a permit. | ||
| * @returns The number of waiting callers. | ||
| */ | ||
| get waitingCount(): number { | ||
| return this.waiting.length; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Processes items concurrently with a specified limit. | ||
| * @param items Array of items to process. | ||
| * @param concurrencyLimit Maximum number of concurrent operations. | ||
| * @param processor Function to process each item. | ||
| * @returns Promise that resolves to array of results. | ||
| */ | ||
| export async function processConcurrently<T, R>( | ||
| items: T[], | ||
| concurrencyLimit: number, | ||
| processor: (item: T) => Promise<R>, | ||
| ): Promise<R[]> { | ||
| const semaphore = new Semaphore(concurrencyLimit); | ||
|
|
There was a problem hiding this comment.
Semaphore / processConcurrently() accept any permits / concurrencyLimit value, including 0 or negative numbers. With concurrencyLimit <= 0, acquire() will never resolve and processConcurrently() will hang forever. Add input validation (e.g., throw a RangeError) to ensure the limit is a positive integer.
| async getExports(filePath: string): Promise<IParsedExport[]> { | ||
| const stats = await this.fileSystemService.getFileStats(filePath); | ||
| const currentMtime = stats.mtime.getTime(); | ||
|
|
||
| // Check cache first | ||
| const cached = this.cache.get(filePath); | ||
| if (cached?.mtime === currentMtime) { | ||
| return cached.exports; | ||
| } | ||
|
|
||
| // Parse and cache the exports | ||
| const content = await this.fileSystemService.readFile(filePath); | ||
| const exports = this.exportParser.extractExports(content); | ||
|
|
There was a problem hiding this comment.
ExportCache.getExports() calls fileSystemService.getFileStats() and then fileSystemService.readFile(). In this PR, FileSystemService.readFile() also performs its own stat() call for the size check, so each cache miss does two stat() syscalls per file. Consider returning size (and possibly mtime) from getFileStats() and allowing readFile() to accept pre-fetched stats (or provide a readFileUnchecked() used by the cache) to avoid the duplicate IO while keeping the size guard.
| private async buildBarrelContent( | ||
| directoryPath: string, | ||
| entries: Map<string, BarrelEntry>, | ||
| barrelFilePath: string, | ||
| hasExistingIndex: boolean, | ||
| options: NormalizedGenerationOptions, | ||
| tsFiles: string[], | ||
| subdirectories: string[], | ||
| ): Promise<string> { | ||
| // Determine what extension to use for exports | ||
| const exportExtension = await this.determineExportExtension(barrelFilePath, hasExistingIndex); | ||
|
|
||
| const newContent = await this.barrelContentBuilder.buildContent( | ||
| entries, | ||
| directoryPath, | ||
| exportExtension, | ||
| ); | ||
|
|
||
| if (!hasExistingIndex || options.mode !== BarrelGenerationMode.UpdateExisting) { | ||
| if (!hasExistingIndex) { | ||
| return newContent; | ||
| } | ||
|
|
||
| return this.mergeWithSanitizedExistingContent( | ||
| newContent, | ||
| barrelFilePath, | ||
| directoryPath, | ||
| tsFiles, | ||
| subdirectories, | ||
| ); | ||
| return this.mergeWithSanitizedExistingContent(newContent, barrelFilePath); | ||
| } |
There was a problem hiding this comment.
buildBarrelContent() now merges existing barrel content whenever index.ts exists, regardless of generation mode. Because BarrelContentSanitizer preserves any non-parent re-export that won’t be regenerated, this can keep stale/invalid re-export lines (e.g., pointing at files that were deleted) during the default CreateOrUpdate flow. If CreateOrUpdate is intended to “refresh exports”, consider only preserving direct definitions/comments (not old re-exports) unless mode === UpdateExisting, or validate preserved re-export targets still exist before keeping them.
| /** | ||
| * Checks if a line is an export statement using AST parsing. | ||
| * @param line The line to check. | ||
| * @returns True if the line is an export statement. | ||
| */ | ||
| export function isExportLine(line: string): boolean { | ||
| const path = extractExportPath(line); | ||
| return path !== null; | ||
| } |
There was a problem hiding this comment.
The docstring for isExportLine() says it checks export statements “using AST parsing”, but the implementation is regex-based via extractExportPath(). Update the comment to match the actual approach so readers don’t assume an AST dependency/behavior.
…relContentSanitizer
Pull Request
Description
This pull request introduces several improvements to repository documentation and packaging configuration, focusing on clarity, maintainability, and ensuring only the intended files are included in published VSIX artifacts. The most important changes are grouped below.
Documentation improvements:
AGENTS.md: Expanded to cover CI checks, packaging conventions, and a publish allowlist; clarified test conventions and added guidance for updating docs alongside automation changes.ARCHITECTURE.md: Rewritten for clarity, reflecting current module structure, command flow, barrel generation orchestration, and extensibility points.Packaging configuration:
.vscodeignore: Switched to an allowlist strategy, ignoring everything by default and explicitly including only essential files for VSIX publishing.Linting configuration:
.markdownlint.yml: Disabled duplicate heading checks to accommodate common changelog patterns.Changelog update:
CHANGELOG.md: Added detailed entries for v1.1.1 and v1.1.0, documenting fixes, new features, test coverage, and packaging changes.Type of Change
Checklist
npm run lintandnpm run format:checkRelated Issues