Skip to content

feat(check): flag __dirname/__filename and suggest ESM path APIs#590

Merged
james-elicx merged 6 commits intomainfrom
opencode/clever-tiger
Mar 19, 2026
Merged

feat(check): flag __dirname/__filename and suggest ESM path APIs#590
james-elicx merged 6 commits intomainfrom
opencode/clever-tiger

Conversation

@james-elicx
Copy link
Collaborator

@james-elicx james-elicx commented Mar 19, 2026

Summary

  • Adds `__dirname` / `__filename` detection to `checkConventions` in `vinext check`
  • Reports an `unsupported` compatibility item listing affected files when CJS globals are found in server source (there is no vinext shim for these — they are a hard runtime failure)
  • Uses a single-pass alternation regex to skip string literals, template literals, and comments in one pass — identifiers inside those tokens are never matched, including inside `${...}` template expressions
  • Merges the new scan into the existing ViewTransition file loop so each source file is read only once
  • Fixes `formatReport` to list affected files under each item in the "Issues to address" section

Closes #583

Migration hint shown to users

`__dirname` / `__filename` are CommonJS globals unavailable in ESM — use `fileURLToPath(import.meta.url)` / `dirname(...)`, or `import.meta.dirname` / `import.meta.filename` (Node 22+)

Tests

9 new tests in `tests/check.test.ts` covering:

  • `__dirname` detected in a server file
  • `__filename` detected independently
  • Both globals in one file produce a single check item with `unsupported` status
  • `__dirname` inside a string literal → not flagged
  • `__dirname` inside a comment → not flagged
  • `__dirname` inside a plain template literal (no interpolation) → not flagged
  • `__dirname` inside a template expression `${__dirname}` → flagged
  • ESM reassignment pattern (`const __dirname = dirname(...)`) → no crash
  • Multiple files each listed in `files`
  • Affected file paths appear in the formatted report's "Issues to address" section

Closes #583

Scan server source files for free uses of the CJS globals __dirname and
__filename and report them as a partial compatibility issue in the
vinext check output.

Uses a single-pass alternation regex that skips string literals,
template literals, and comments in one pass, so identifiers inside
those tokens are never matched.
@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 19, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vinext@590

commit: dee6700

@github-actions
Copy link

Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original

@james-elicx
Copy link
Collaborator Author

/bigbonk review

Copy link
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

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

Good feature — clean implementation, well-tested, and the single-pass alternation regex is a reasonable approach for an advisory scanner. A few observations:

The template expression blind spot is worth knowing about. The template literal branch treats ${...} interpolations as literal content, so `${__dirname}/views` won't be detected. This is the most natural path-building idiom in CJS code (`${__dirname}/templates`), so it's a meaningful false negative for the feature's purpose. Not necessarily a blocker since this is an advisory check, but worth deciding whether to address now or file an issue for later.

The rest of the edge cases (regex literals, property access like obj.__dirname, the ESM reassignment idiom) are minor and acceptable for a migration compatibility scanner.

Tests are thorough and cover the important cases well.

Co-authored-by: james-elicx <james-elicx@users.noreply.github.com>
@ask-bonk
Copy link
Contributor

ask-bonk bot commented Mar 19, 2026

Review posted. Here's a summary of what I found:

PR #590 Review Summary

The implementation is clean and well-tested. The single-pass alternation regex is a solid approach for an advisory scanner — it correctly skips __dirname/__filename inside string literals, line comments, and block comments.

Key finding: The template literal branch has a blind spot for ${...} interpolations. Code like `${__dirname}/views` — which is the most natural CJS path-building pattern — will not be detected because the regex treats template expression contents as literal string content. I left an inline comment on the regex at packages/vinext/src/check.ts:518 with a suggested fix and alternative approaches.

Other minor edge cases (regex literals, obj.__dirname property access, ESM reassignment idiom) are acceptable for an advisory migration check.

Tests: All 79 tests pass. The 7 new tests cover the important positive and negative cases well.

github run

…r match

The previous template literal skip branch consumed `${...}` bodies as
literal content, so __dirname in e.g. `${__dirname}/views` was silently
skipped.

Change the template branch to stop at ${ boundaries by excluding $ from
the non-interpolation character class: [^`\\$]. A bare $ not followed
by { is still allowed via |$(?!\{). This means the ${...} body is never
consumed by the skip branch and __dirname inside template expressions is
correctly caught by the identifier branch.

Adds two new tests: one verifying plain template content (no interpolation)
is still ignored, one verifying __dirname inside ${} is detected.
@james-elicx
Copy link
Collaborator Author

/bigbonk review

@ask-bonk
Copy link
Contributor

ask-bonk bot commented Mar 19, 2026

UnknownError: "Overloaded"

github run

@ask-bonk
Copy link
Contributor

ask-bonk bot commented Mar 19, 2026

@james-elicx Bonk workflow failed. Check the logs for details.

View workflow run · To retry, trigger Bonk again.

@james-elicx
Copy link
Collaborator Author

/bigbonk review

@ask-bonk
Copy link
Contributor

ask-bonk bot commented Mar 19, 2026

UnknownError: "Overloaded"

github run

@james-elicx
Copy link
Collaborator Author

/bigbonk review

@ask-bonk
Copy link
Contributor

ask-bonk bot commented Mar 19, 2026

UnknownError: "Overloaded"

github run

@james-elicx
Copy link
Collaborator Author

/bigbonk review

@ask-bonk
Copy link
Contributor

ask-bonk bot commented Mar 19, 2026

UnknownError: "Overloaded"

github run

…es in issues section

There is no vinext shim for __dirname/__filename in user app code — it
is a hard runtime failure, so partial was wrong. Changed to unsupported.

Also fixed formatReport to list affected files under each unsupported
item in the 'Issues to address' section. Previously files were only
shown in the per-section detail view (imports), leaving the summary
section without the context needed to actually fix the issue.

Adds a test verifying the file path appears in the formatted report.
@james-elicx
Copy link
Collaborator Author

/bigbonk review this pr please

Copy link
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

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

Implementation is correct and well-tested. The regex approach, code integration, and test coverage are all solid.

Regex correctness: The single-pass alternation regex correctly handles:

  • String literals (single/double-quoted), line comments, block comments — all skipped
  • Plain template literals (no interpolation) — skipped via [^\\$]` character class
  • Template expressions ${...} — correctly not consumed by the skip branch (the $(?!\{) lookahead ensures ${ breaks out of the template-content match, exposing the expression body to the identifier branch)
  • Nested template literals — works correctly since inner backtick-delimited content is matched by the template-skip branch, and __dirname in inner ${...} is still exposed

Known acceptable edge case: obj.__dirname (property access) will be flagged as a false positive. This is fine for an advisory migration scanner — the \b word boundary matches but can't distinguish property access from a free identifier without a full parser. The PR description and previous review both acknowledge this.

ESM reassignment pattern (const __dirname = dirname(fileURLToPath(import.meta.url))) — the test correctly notes this will be detected (the identifier appears on the LHS of assignment). The test only asserts no crash, which is the right call. Users writing this pattern are already on the ESM path, so a spurious mention in the report is low-harm.

Integration with existing code: Clean merge into the existing file loop — each file is read once, rel is computed once and reused for both ViewTransition and CJS-global tracking. The cjsGlobalScanRegex.lastIndex = 0 reset before each file is correct since the regex has the g flag.

formatReport fix: The file-listing addition under "Issues to address" is straightforward and tested.

Unrelated files: The 4 files under examples/fumadocs-docs-template/.source/ appear to be generated fumadocs output that got included in this PR. They're unrelated to the __dirname/__filename detection feature. Consider splitting them into a separate commit or PR for cleaner history, but not a blocker.

Test coverage: 10 new tests cover positive detection, false-negative avoidance (strings, comments, plain templates), template expression detection, multi-file tracking, ESM reassignment resilience, and the formatReport file listing. Thorough.

@ask-bonk
Copy link
Contributor

ask-bonk bot commented Mar 19, 2026

I've reviewed and approved PR #590. Here's a summary:

Verdict: Approved

The implementation is correct and well-tested. Key findings:

  • Regex is solid. I manually verified it handles all the important cases: strings, comments, plain templates (skipped), template expressions like `${__dirname}/views` (correctly detected), and even nested templates. The $(?!\{) lookahead fix for template expressions works as intended.

  • Known acceptable false positive: obj.__dirname property access will be flagged. Fine for an advisory scanner — distinguishing property access from free identifiers requires a full parser.

  • Clean integration: The scan merges into the existing file loop, reads each file once, reuses the relative path, and properly resets lastIndex on the global regex.

  • Minor note: The 4 examples/fumadocs-docs-template/.source/ files appear unrelated to this feature (generated fumadocs output). Suggested splitting for cleaner history but not a blocker.

github run

@james-elicx james-elicx marked this pull request as ready for review March 19, 2026 16:19
@james-elicx james-elicx enabled auto-merge (squash) March 19, 2026 16:19
@james-elicx james-elicx merged commit 7f30707 into main Mar 19, 2026
25 checks passed
@james-elicx james-elicx deleted the opencode/clever-tiger branch March 19, 2026 16:23
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.

vinext check should flag __dirname/__filename and suggest ESM path APIs

1 participant