Skip to content

refactor(copilot-rule): reuse toPosixPath and switch sameRelativePath to object params#1644

Merged
dyoshikawa merged 2 commits into
dyoshikawa:mainfrom
Chen17-sq:refactor/1603-reuse-toposixpath-in-copilot-rule
May 20, 2026
Merged

refactor(copilot-rule): reuse toPosixPath and switch sameRelativePath to object params#1644
dyoshikawa merged 2 commits into
dyoshikawa:mainfrom
Chen17-sq:refactor/1603-reuse-toposixpath-in-copilot-rule

Conversation

@Chen17-sq
Copy link
Copy Markdown
Contributor

Summary

Addresses the refactor checklist in #1603 for src/features/rules/copilot-rule.ts:

  • DRY — Replace the local path.replace(/\\/g, "/") with the shared toPosixPath utility from src/utils/file.ts, in line with the coding guideline that points to toPosixPath for non-filesystem path normalization.
  • Keep the slash collapse, but justify it — The follow-up replace(/\/+/g, "/") is still needed: on POSIX, node:path.join(".github\\", "x.md") keeps the trailing backslash literal, so after toPosixPath runs we get .github//x.md. A short comment now spells this out so future readers don't think the collapse is dead code.
  • Avoid the path parameter name — Rename pathp so it no longer shadows the node:path import in the same file.
  • Object-based sameRelativePath — Switch the four positional strings to {dir, file} objects per the project's coding guidelines, and update both call sites (fromFile, forDeletion).

Test plan

  • pnpm cicheck (fmt + oxlint + eslint + tsgo + vitest 5552 tests + cspell + secretlint) passes locally on Node 22.
  • New forDeletion tests:
    • Root deletion target is detected when relativeDirPath is ".github\\".
    • Non-root paths with a mid-path backslash (".github\\instructions") stay non-root.
  • Existing root-detection test for .github\\ continues to pass — behavior is preserved, only the helper implementation changes.

Refs #1603.

… to object params

Addresses the review findings tracked in dyoshikawa#1603:

- Replace the local backslash regex with the shared toPosixPath utility, keeping the consecutive-slash collapse and explaining the WSL edge case it covers

- Rename the parameter from path to p to avoid shadowing the node:path import

- Move sameRelativePath from four positional strings to {dir, file} objects per the coding guidelines and update both call sites

- Add forDeletion tests that pin down root detection with a trailing backslash separator and confirm non-root paths with mid-path backslashes stay non-root

Refs dyoshikawa#1603.
Copy link
Copy Markdown
Owner

@dyoshikawa dyoshikawa left a comment

Choose a reason for hiding this comment

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

Nice, focused refactor — it matches the coding guidelines (object params, shared toPosixPath) cleanly, and the new forDeletion tests cover the mixed-separator edge case well. One small thing about the comment accuracy flagged inline.

Comment thread src/features/rules/copilot-rule.ts Outdated
path.replace(/\\/g, "/").replace(/\/+/g, "/");
// toPosixPath converts backslashes to forward slashes so paths compare equally
// on Windows and POSIX. The extra slash collapse covers a WSL/mixed-separator
// edge case where `node:path/posix.join` keeps a literal backslash inside an
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Minor: this references node:path/posix.join, but the actual import at the top of the file is node:path (import { join } from "node:path"). On POSIX, node:path.join(".github\\", "x.md") keeps the backslash literal, which is what the slash-collapse handles. The PR description has the accurate phrasing — consider updating the comment to say node:path.join on POSIX so future readers do not get confused.

// edge case where `node:path/posix.join` keeps a literal backslash inside an
// input segment (e.g. ".github\\") and produces ".github//instructions/x.md"
// after the backslash is rewritten.
const normalizeRelativePath = (p: string): string => toPosixPath(p).replace(/\/+/g, "/");
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Nit: p works for avoiding the node:path shadow, but relativePath would communicate intent a bit better. Feel free to ignore — toPosixPath in utils/file.ts already uses p, so this is at least consistent with the existing helper.

Copy link
Copy Markdown
Owner

@dyoshikawa dyoshikawa left a comment

Choose a reason for hiding this comment

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

Clean little refactor. The DRY consolidation onto toPosixPath, the pathp rename, and the {dir, file} object form on sameRelativePath all match the project's coding guidelines, and the slash-collapse stays put with a justification — that part is exactly right. CI is fully green across all four platforms.

Two things worth a second look before merge. The new comment block in copilot-rule.ts describes node:path/posix.join, but the file actually imports from node:path, and the example path it shows doesn't match the string that's actually joined at the call sites. And the new forDeletion tests are labeled "mixed separators" but don't actually mix / and \. Both are non-blocking nits, but worth tightening for future readers. Optionally, a symmetric fromFile case for the mid-path backslash would close the coverage gap that #1603 explicitly called out.

Comment thread src/features/rules/copilot-rule.ts Outdated
path.replace(/\\/g, "/").replace(/\/+/g, "/");
// toPosixPath converts backslashes to forward slashes so paths compare equally
// on Windows and POSIX. The extra slash collapse covers a WSL/mixed-separator
// edge case where `node:path/posix.join` keeps a literal backslash inside an
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The comment refers to node:path/posix.join, but the import at the top of this file is node:path (the platform-aware variant), not node:path/posix. The behavior it describes — node:path.join(".github\\", "x.md") leaving the backslash literal — happens on POSIX where \ isn't a separator, and that's actually what we rely on here. Also the example output mentions .github//instructions/x.md, but the join at the call sites in this file produces .github//copilot-instructions.md for the root case. Mind tightening the comment so it matches the real import and the real string under test? Otherwise the next reader may go hunting for a path/posix import that doesn't exist.

Comment thread src/features/rules/copilot-rule.test.ts Outdated
});

describe("forDeletion", () => {
it("should mark root deletion target when separators are mixed", () => {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Small naming nit: both new cases say "separators are mixed" / "mixed separators", but neither input actually mixes / and \ in the same string — they use backslashes only (trailing here, mid-path below). Maybe rename to something like "should mark root deletion target when the dir uses a trailing backslash" and "should treat non-root paths with backslash separators as non-root"? Or, if you'd like to keep the phrasing, add one truly mixed case like relativeDirPath: ".github\\instructions/sub" — that matches what #1603 explicitly asked for.

});
});

describe("forDeletion", () => {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Nice that forDeletion now has coverage for the backslash path. A symmetric test for fromFile with relativeDirPath: ".github\\instructions" would close the other half of the issue's coverage ask, since fromFile also routes through sameRelativePath. Not blocking — the helper is shared — but adding it would make the safety net explicit at both call sites.

… mid-path test

Addresses dyoshikawa's review on dyoshikawa#1644:

- Comment block on normalizeRelativePath previously said 'node:path/posix.join' and showed a multi-segment example, but the file imports node:path and sameRelativePath joins dir + single filename. Rewrote the comment so the module reference matches the import line and the example matches the actual call shape ('.github\\' + 'copilot-instructions.md').

- forDeletion tests renamed: 'when separators are mixed' → 'when relativeDirPath has a trailing backslash' / 'when relativeDirPath has a mid-path backslash'. They never actually mixed / and \\; the new labels describe what's really exercised.

- Same rename applied to the existing fromFile 'should normalize separators…' test.

- Added the optional symmetric fromFile mid-path backslash test the review flagged as closing the coverage gap dyoshikawa#1603 originally identified. Materializes the fixture directory at the literal '.github\\instructions' name since fromFile reads from the raw (un-normalized) relativeDirPath after the root check.
@Chen17-sq
Copy link
Copy Markdown
Contributor Author

Thanks for the thorough read — pushed 69cde2f addressing all three:

  • Comment accuracy. Rewrote the normalizeRelativePath block so the module reference matches the actual node:path import on line 1, and the example matches the actual call shape (.github\\ joined with copilot-instructions.md) instead of the multi-segment path the previous version implied.
  • Test labeling. Renamed both forDeletion tests and the existing fromFile "normalize separators" test to spell out what they actually exercise — "trailing backslash" vs "mid-path backslash" — since none of them really mix / and \\.
  • Symmetric fromFile mid-path test. Added the optional one. Since fromFile reads from the raw (un-normalized) relativeDirPath after the root check, the fixture materializes the directory at the literal .github\\instructions name on disk so the read succeeds — that path comment is inline on the test for future readers.

CI green; 1062 → 1063 tests on the copilot-rule file (65 → 66 in the suite).

@dyoshikawa dyoshikawa merged commit 78cd3ac into dyoshikawa:main May 20, 2026
6 checks passed
@dyoshikawa
Copy link
Copy Markdown
Owner

@Chen17-sq Thank you!

@github-actions github-actions Bot mentioned this pull request May 21, 2026
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.

2 participants