feat(sdk/skyux-eslint)!: add no-barrel-exports rule#4268
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a new SkyUX ESLint rule Changes
Sequence Diagram(s)sequenceDiagram
participant ESLint as ESLint Engine
participant Rule as no-barrel-exports Rule
participant Resolver as resolveModulePath
participant Extractor as getNamedExportsFromFile
participant Fixer as Fix Generator
ESLint->>Rule: Provide ExportAllDeclaration node
Rule->>Rule: Is namespace re-export?
alt Namespace re-export
Rule-->>ESLint: Report noNamespaceReExports
else Plain wildcard export
Rule->>Resolver: Resolve specifier (relative?)
Resolver-->>Rule: file path or undefined
alt Resolved
Rule->>Extractor: Read & extract named exports
Extractor-->>Rule: valueExports / typeExports or undefined
alt Exports found
Rule->>Fixer: Build explicit export statements
Fixer-->>Rule: replacement code
Rule-->>ESLint: Report noBarrelExports with fix
else No exports
Rule-->>ESLint: Report noBarrelExports (no fix)
end
else Not resolved
Rule-->>ESLint: Report noBarrelExports (no fix)
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
View your CI Pipeline Execution ↗ for commit d46106b
☁️ Nx Cloud last updated this comment at |
|
Component Storybooks:
Apps: |
There was a problem hiding this comment.
Pull request overview
Adds a new skyux-eslint/no-barrel-exports TypeScript rule to discourage export * (including export * as ns) so library/public API surfaces stay explicit, and wires it into the SKY UX TS plugin + recommended/all presets with accompanying docs and snapshots.
Changes:
- Introduces
no-barrel-exportsrule (with an auto-fix attempt for relativeexport * fromcases). - Adds export-resolution utilities (+ unit tests) to support the rule’s fixer.
- Enables the rule in
ts-recommendedandts-all, and adds rule documentation + snapshot updates.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| libs/sdk/skyux-eslint/src/rules/utils/resolve-exports.ts | Adds filesystem/path helpers and regex-based export extraction used by the rule fixer. |
| libs/sdk/skyux-eslint/src/rules/utils/resolve-exports.spec.ts | Adds unit tests for module path resolution and export extraction. |
| libs/sdk/skyux-eslint/src/rules/no-barrel-exports.ts | Implements the new rule and fixer for wildcard re-exports. |
| libs/sdk/skyux-eslint/src/rules/no-barrel-exports.spec.ts | Adds RuleTester coverage including fixer behavior via mocks. |
| libs/sdk/skyux-eslint/src/plugins/ts-plugin.ts | Registers the new rule in the TS plugin. |
| libs/sdk/skyux-eslint/src/configs/ts-recommended.ts | Enables the rule in the recommended TS preset. |
| libs/sdk/skyux-eslint/src/configs/ts-all.ts | Enables the rule in the “all” TS preset. |
| libs/sdk/skyux-eslint/docs/rules/no-barrel-exports.md | Adds end-user documentation for the new rule. |
| libs/sdk/eslint-config-skyux/src/snapshots/index.test.ts.snap | Updates snapshots to include the new rule + preset changes. |
You can also share your feedback on Copilot code review. Take the survey.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@libs/sdk/skyux-eslint/docs/rules/no-barrel-exports.md`:
- Around line 18-44: The markdown headings under the "Usage Examples" section
use fourth-level headings (e.g., "#### Default Config", "#### :x: Invalid Code",
"#### :white_check_mark: Valid Code") which breaks the heading hierarchy; change
those subsection headings from "####" to "###" so they are peer-level
subsections under the parent "Usage Examples" heading to satisfy markdownlint
MD001.
In `@libs/sdk/skyux-eslint/src/rules/no-barrel-exports.ts`:
- Around line 52-60: The autofixer currently emits a single value re-export for
everything returned by getNamedExportsFromFile, which incorrectly treats
type-only symbols as value exports; update the fixer logic around
node/moduleSpecifier to split the returned names into two arrays (valueExports
vs typeOnlyExports) by using the same logic as extractNamedExports or by
enhancing getNamedExportsFromFile to indicate kind, then generate appropriate
replacement text: emit "export { ... } from '<moduleSpecifier>';" for
valueExports and "export type { ... } from '<moduleSpecifier>';" for
typeOnlyExports (emit one or both statements depending on which arrays are
non-empty), and use fixer.replaceText(node, ...) with that combined string.
Ensure you reference the existing functions getNamedExportsFromFile and
extractNamedExports (or augment getNamedExportsFromFile to return kind info) and
keep node and moduleSpecifier usage intact.
In `@libs/sdk/skyux-eslint/src/rules/utils/resolve-exports.ts`:
- Around line 39-44: The current declarationRegex in resolve-exports.ts only
captures the first identifier (match[1]) which loses subsequent declarators in
statements like `export const a = 1, b = 2;`; update the logic that iterates
over declarationRegex.exec(fileContent) to detect when the matched kind is a
variable declaration (const|let|var), then parse the full declarator list from
the matched text (split on commas outside brackets/strings or use a simple split
for typical patterns) and push each identifier into the exports array; keep
existing behavior for class/interface/type/function/enum by continuing to push
match[1] for those kinds and ensure the variables-handling code references the
declarationRegex match group and fileContent to extract the entire declaration
text before splitting.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 97a92598-eeb2-4924-862f-12b8c247bf19
⛔ Files ignored due to path filters (1)
libs/sdk/eslint-config-skyux/src/__snapshots__/index.test.ts.snapis excluded by!**/*.snap
📒 Files selected for processing (8)
libs/sdk/skyux-eslint/docs/rules/no-barrel-exports.mdlibs/sdk/skyux-eslint/src/configs/ts-all.tslibs/sdk/skyux-eslint/src/configs/ts-recommended.tslibs/sdk/skyux-eslint/src/plugins/ts-plugin.tslibs/sdk/skyux-eslint/src/rules/no-barrel-exports.spec.tslibs/sdk/skyux-eslint/src/rules/no-barrel-exports.tslibs/sdk/skyux-eslint/src/rules/utils/resolve-exports.spec.tslibs/sdk/skyux-eslint/src/rules/utils/resolve-exports.ts
…fixer (#4269) The `no-barrel-exports` auto-fixer always emitted `export { ... }` for all symbols, which is invalid under `isolatedModules: true` for type-only exports (interfaces, type aliases). This splits the fixer output into separate `export { ... }` and `export type { ... }` statements. ## `resolve-exports.ts` - Introduces `ExtractedNamedExports` interface with `valueExports: string[]` and `typeExports: string[]` - Rewrites `extractNamedExports` to categorize by kind: - **Value**: `class`, `abstract class`, `function`, `async function`, `enum`, `const`/`let`/`var` (including `declare` variants) - **Type-only**: `interface`, `type` alias, `export type { ... }` blocks, inline `type` specifiers in `export { type Foo, Bar }` - Adds multi-declarator variable support: `export const a = 1, b = 2` → `['a', 'b']` - Uses `[^;\n]+` in variable regex to prevent greedy cross-statement capture in semicolon-free files - Updates `getNamedExportsFromFile` return type to `ExtractedNamedExports | undefined` ## `no-barrel-exports.ts` - Fixer now emits one or both statements depending on what's present: ```ts // Before (always emitted as values — broken for interfaces/types): export { FooComponent, FooConfig, FooType } from './foo'; // After: export { FooComponent } from './foo'; export type { FooConfig, FooType } from './foo'; ``` - Updates `meta.docs.description` to mention `export * as ns from` alongside `export * from` ## Tests & docs - Updates all `extractNamedExports` expectations to `{ valueExports, typeExports }` shape - Adds tests for type-only re-exports, mixed exports, multi-declarator variables, inline `type` specifiers, and trailing-comma specifiers - Replaces `require('node:fs').mkdirSync` with the already-imported `mkdirSync` - Fixes `no-barrel-exports.md` heading levels (`####` → `###`) per markdownlint MD001 > [!WARNING] > > <details> > <summary>Firewall rules blocked me from connecting to one or more addresses (expand for details)</summary> > > #### I tried to connect to the following addresses, but was blocked by firewall rules: > > - `download.cypress.io` > - Triggering command: `/home/REDACTED/work/_temp/ghcca-node/node/bin/node node dist/index.js --exec install -test` (dns block) > > If you need me to access, download, or install something from one of these locations, you can either: > > - Configure [Actions setup steps](https://gh.io/copilot/actions-setup-steps) to set up my environment, which run before the firewall is enabled > - Add the appropriate URLs or hosts to the custom allowlist in this repository's [Copilot coding agent settings](https://github.com/blackbaud/skyux/settings/copilot/coding_agent) (admins only) > > </details> <!-- START COPILOT CODING AGENT TIPS --> --- 💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey). --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: johnhwhite <750350+johnhwhite@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@libs/sdk/skyux-eslint/src/rules/utils/resolve-exports.ts`:
- Around line 64-69: The current export variable parsing
(variableDeclarationRegex / fileContent and the names extraction) wrongly splits
on every comma and stops at newlines, mis-parsing declarations with nested
commas or multiline initializers; change the approach to capture the full
declaration up to the semicolon (e.g. adjust the regex to consume everything
until the next semicolon rather than stopping at newlines) and replace the naive
.split(',') with a small stateful splitter that iterates the captured
declaration and ignores commas inside parentheses/brackets/braces and inside
strings/comments, then apply the identifier extraction (/^([A-Za-z_$][\w$]*)/)
to each top-level segment to build names so variableDeclarationRegex, the names
mapping logic, and downstream code get correct symbols.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5cf22840-63c5-45f7-856a-bb945db66909
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (5)
libs/sdk/skyux-eslint/docs/rules/no-barrel-exports.mdlibs/sdk/skyux-eslint/src/rules/no-barrel-exports.spec.tslibs/sdk/skyux-eslint/src/rules/no-barrel-exports.tslibs/sdk/skyux-eslint/src/rules/utils/resolve-exports.spec.tslibs/sdk/skyux-eslint/src/rules/utils/resolve-exports.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- libs/sdk/skyux-eslint/docs/rules/no-barrel-exports.md
- libs/sdk/skyux-eslint/src/rules/no-barrel-exports.spec.ts
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
You can also share your feedback on Copilot code review. Take the survey.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
libs/sdk/skyux-eslint/src/rules/utils/resolve-exports.spec.ts (1)
12-20: Deduplicate temp-dir lifecycle setup to reduce test maintenance.The
beforeEach/afterEachtemp-dir setup is repeated in both suites; extracting a shared helper (or using outer hooks) would keep setup changes in one place.Also applies to: 52-58
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@libs/sdk/skyux-eslint/src/rules/utils/resolve-exports.spec.ts` around lines 12 - 20, Extract the duplicated temp-dir lifecycle into a single shared setup so both test suites reuse it: move the tempDir declaration and the mkdtempSync/rmSync beforeEach/afterEach into an outer scope or helper function and have both inner describe blocks use that shared hook; specifically consolidate the existing tempDir variable and the beforeEach (mkdtempSync(join(tmpdir(), 'resolve-exports-'))) and afterEach (rmSync(tempDir, { recursive: true })) logic so you only maintain it once (referencing tempDir, mkdtempSync, rmSync, and the beforeEach/afterEach hooks in resolve-exports.spec.ts).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@libs/sdk/skyux-eslint/src/rules/utils/resolve-exports.ts`:
- Around line 9-15: The resolveModulePath function currently resolves any
moduleSpecifier as a filesystem path; add an early guard in resolveModulePath to
enforce the "relative specifier" contract by returning undefined unless
moduleSpecifier begins with "./" or "../" (so only relative paths are
processed), then continue with the existing dirname/resolve logic; refer to the
resolveModulePath function to locate where to insert this check.
---
Nitpick comments:
In `@libs/sdk/skyux-eslint/src/rules/utils/resolve-exports.spec.ts`:
- Around line 12-20: Extract the duplicated temp-dir lifecycle into a single
shared setup so both test suites reuse it: move the tempDir declaration and the
mkdtempSync/rmSync beforeEach/afterEach into an outer scope or helper function
and have both inner describe blocks use that shared hook; specifically
consolidate the existing tempDir variable and the beforeEach
(mkdtempSync(join(tmpdir(), 'resolve-exports-'))) and afterEach (rmSync(tempDir,
{ recursive: true })) logic so you only maintain it once (referencing tempDir,
mkdtempSync, rmSync, and the beforeEach/afterEach hooks in
resolve-exports.spec.ts).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: dacb5c4a-5b36-4c92-bd60-eebcc098fbb1
📒 Files selected for processing (5)
libs/sdk/skyux-eslint/docs/rules/no-barrel-exports.mdlibs/sdk/skyux-eslint/src/rules/no-barrel-exports.spec.tslibs/sdk/skyux-eslint/src/rules/no-barrel-exports.tslibs/sdk/skyux-eslint/src/rules/utils/resolve-exports.spec.tslibs/sdk/skyux-eslint/src/rules/utils/resolve-exports.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- libs/sdk/skyux-eslint/src/rules/no-barrel-exports.ts
- libs/sdk/skyux-eslint/src/rules/no-barrel-exports.spec.ts
## [14.0.0-alpha.5](14.0.0-alpha.4...14.0.0-alpha.5) (2026-03-05) ### ⚠ BREAKING CHANGES * **sdk/skyux-eslint:** add no-barrel-exports rule (#4268) ### Features * **components/ag-grid:** use datepicker component for column filter ([#4249](#4249)) ([d026f28](d026f28)), closes [AB#3648062](https://dev.azure.com/blackbaud/Products/_workitems/edit/3648062) * **sdk/skyux-eslint:** add no-barrel-exports rule ([#4268](#4268)) ([d7a8332](d7a8332)) ### Bug Fixes * update to `@angular/build@21.2.0` ([#4266](#4266)) ([e2c8ec0](e2c8ec0)) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Breaking Changes** * New ESLint rule added (no-barrel-exports) * **New Features** * Integrated datepicker component for ag-grid column filtering * Added new ESLint rule for code organization * **Bug Fixes** * Updated Angular build version <!-- end of auto-generated comment: release notes by coderabbit.ai -->
[AB#3614172](https://dev.azure.com/blackbaud/f565481a-7bc9-4083-95d5-4f953da6d499/_workitems/edit/3614172) Reverts #4268 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Removed the no-barrel-exports ESLint rule from configs and the plugin * Deleted the rule implementation, related utility module, tests, and documentation * Removed the TypeScript peer dependency declaration from the package configuration <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## [14.0.0-alpha.10](14.0.0-alpha.9...14.0.0-alpha.10) (2026-03-18) ### ⚠ BREAKING CHANGES * **components/layout:** replace `@angular/animations` in text expand repeater component with CSS transitions (#4317) * **components/layout:** replace `@angular/animations` in text expand component with CSS transitions (#4308) * **components/inline-form:** replace `@angular/animations` with CSS transitions (#4315) * **components/popovers:** replace `@angular/animations` with CSS transitions (#4313) ### Features * **components/modals:** modal header close button is shown in modern theme ([#4265](#4265)) ([db2a615](db2a615)) ### Bug Fixes * **components/core:** animationend and transitionend handlers detect suppressed animations via `getComputedStyle` ([#4310](#4310)) ([88d7b0f](88d7b0f)) * **components/inline-form:** replace `@angular/animations` with CSS transitions ([#4315](#4315)) ([5254dbb](5254dbb)) * **components/layout:** replace `@angular/animations` in text expand component with CSS transitions ([#4308](#4308)) ([bbde359](bbde359)) * **components/layout:** replace `@angular/animations` in text expand repeater component with CSS transitions ([#4317](#4317)) ([bb4a3c7](bb4a3c7)) * **components/popovers:** replace `@angular/animations` with CSS transitions ([#4313](#4313)) ([28088aa](28088aa)) * **sdk/skyux-eslint:** remove `no-barrel-exports` rule ([#4320](#4320)) ([73dafd6](73dafd6)), closes [AB#3614172](https://dev.azure.com/blackbaud/Products/_workitems/edit/3614172) [#4268](#4268) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Breaking Changes** * Animation behavior updated across multiple components * **New Features** * Added close button to modal headers in the modern theme * **Bug Fixes** * Resolved animation handling issues affecting multiple components * Fixed related stability issues across components <!-- end of auto-generated comment: release notes by coderabbit.ai -->
AB#3614172
Summary by CodeRabbit
New Features
Documentation
Tests
Chores