Skip to content

refactor(commands): extract getRootOpts() helper#89

Open
qorexdev wants to merge 1 commit intolinearis-oss:mainfrom
qorexdev:refactor/extract-root-opts-helper
Open

refactor(commands): extract getRootOpts() helper#89
qorexdev wants to merge 1 commit intolinearis-oss:mainfrom
qorexdev:refactor/extract-root-opts-helper

Conversation

@qorexdev
Copy link
Copy Markdown
Contributor

@qorexdev qorexdev commented Apr 7, 2026

Replaces all 25 instances of command.parent!.parent!.opts() with a getRootOpts(command) helper that walks the Commander tree to the root.

Eliminates non-null assertions and makes command nesting changes safe.

Closes #61

@qorexdev qorexdev requested a review from iamfj as a code owner April 7, 2026 20:52
Copilot AI review requested due to automatic review settings April 7, 2026 20:52
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors CLI command handlers to stop relying on a fragile fixed command.parent!.parent!.opts() chain by introducing a getRootOpts(command) helper that walks the Commander parent tree to the root before reading options.

Changes:

  • Added getRootOpts(command: Command) helper in src/common/context.ts to retrieve root command options safely.
  • Updated command action handlers across multiple command modules to call getRootOpts(command) instead of hardcoded parent navigation.
  • Updated unit tests’ context.js mocks to preserve the real module exports while overriding specific functions.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/unit/commands/issues.test.ts Updates context.js mock to keep real exports while stubbing createContext.
tests/unit/commands/auth.test.ts Updates context.js mock to keep real exports while stubbing createGraphQLClient.
src/common/context.ts Adds getRootOpts() helper for resolving root Commander options.
src/commands/users.ts Uses getRootOpts(command) when creating context.
src/commands/teams.ts Uses getRootOpts(command) when creating context.
src/commands/projects.ts Uses getRootOpts(command) when creating context.
src/commands/milestones.ts Uses getRootOpts(command) across milestone actions.
src/commands/labels.ts Uses getRootOpts(command) when creating context.
src/commands/issues.ts Uses getRootOpts(command) across issue actions.
src/commands/files.ts Uses getRootOpts(command) to obtain root opts for API token resolution.
src/commands/documents.ts Uses getRootOpts(command) to obtain root opts before creating context.
src/commands/cycles.ts Uses getRootOpts(command) across cycle actions.
src/commands/comments.ts Uses getRootOpts(command) when creating context.
src/commands/auth.ts Uses getRootOpts(command) to resolve root opts for auth flows.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +9 to +10
let current: Command | null = command;
while (current?.parent) {
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getRootOpts declares current as Command | null and then returns current.opts(). With tsconfig.json strict: true, current remains possibly null after the loop (because current = current.parent can assign a nullable type), so this should fail type-checking/build. Consider making current non-nullable (e.g., initialize as let current = command; and loop while current.parent), or assert non-null at the end after proving it via the loop condition.

Suggested change
let current: Command | null = command;
while (current?.parent) {
let current = command;
while (current.parent) {

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

@iamfj iamfj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean refactoring — does exactly what #61 asked for, no more no less. All 25 parent!.parent! chains replaced, zero remaining. The helper is well-placed, well-named, and the mock updates in the test files are done correctly with importOriginal.

Two small suggestions (non-blocking):

  1. Typing simplification — see inline comment. The Command | null annotation is unnecessary and could be simplified.

  2. Unit test for getRootOpts() — it's a new exported function in common/. It's tested indirectly through command tests, but a small dedicated test in tests/unit/common/ would be a nice addition — especially the edge case where the command is already the root. Happy to merge as-is and add it as a follow-up.

Nice first contribution 👍

export type { CommandOptions };

export function getRootOpts(command: Command): CommandOptions {
let current: Command | null = command;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: current doesn't need to be Command | null — it starts as command (non-null) and the loop only assigns current.parent when it's truthy. You can simplify to:

let current = command;
while (current.parent) {
  current = current.parent;
}

This drops the optional chaining and keeps the type as Command throughout — no null narrowing needed, and current.opts() is unambiguously safe.

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: extract getRootOpts() helper to replace fragile parent chain navigation

3 participants