fix(linter/plugins): trim leading newline for partial sources#20928
Conversation
Merging this PR will not alter performance
Comparing Footnotes
|
There was a problem hiding this comment.
Hi, thanks for working on this, can you add a test inside:
/apps/oxlint/test/fixtures/
This will be a napi test, that tests the JS plugins, you'll need to configure it - vue file, oxlint config etc - look at the other folders in that repo for examples
camc314
left a comment
There was a problem hiding this comment.
See previous msg - needs test coverage.
|
@camc314 thanks for review! added! |
|
Thanks - mind fixing the CI failure? |
|
@camc314 It took some effort, but the CI passed. I think the docs are a separate issue |
Applied from oxc-project#20928. Fixes false positives in Vue/Svelte/Astro files where the newline after `<script>` was included in the partial source. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Conflicts: # crates/oxc_linter/src/context/host.rs
camc314
left a comment
There was a problem hiding this comment.
thanks!
@overlookmotel if you wouldn't mind taking a retrospective look that would be amazing!
There was a problem hiding this comment.
Pull request overview
This PR fixes a false-positive from JS-plugin-based rules (notably @stylistic/no-multiple-empty-lines) when linting “partial sources” extracted from framework files (e.g. Vue <script> blocks), where an implicit leading newline was being treated as a blank line at BOF.
Changes:
- Trim a single leading
\nor\r\nfrom partial-sourceprogram.source_textbefore running JS plugins, and adjust UTF-8⇄UTF-16 span conversion offsets accordingly. - Expose
ContextSubHost::source_text_offset()for use outside thecontextmodule. - Add an oxlint CLI fixture that runs a JS plugin against a
.vuepartial source and snapshots the output (ensuring no BOF blank-line diagnostic is reported).
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| crates/oxc_linter/src/lib.rs | Trims an implicit leading newline for partial sources before JS-plugin linting and updates span conversion offset logic. |
| crates/oxc_linter/src/context/host.rs | Adds a crate-visible accessor for source_text_offset to support partial-source handling outside the module. |
| apps/oxlint/test/fixtures/js_config_js_plugins_vue_partial_source/plugin.ts | Adds a minimal JS plugin fixture rule that detects BOF/consecutive blank lines via context.sourceCode.lines. |
| apps/oxlint/test/fixtures/js_config_js_plugins_vue_partial_source/oxlint.config.ts | Configures the fixture to load the local JS plugin and enforce strict empty-line limits. |
| apps/oxlint/test/fixtures/js_config_js_plugins_vue_partial_source/files/test.vue | Provides a Vue SFC input that previously triggered the BOF false-positive due to the implicit leading newline. |
| apps/oxlint/test/fixtures/js_config_js_plugins_vue_partial_source/output.snap.md | Snapshots expected CLI output (no BOF error, only the intended blank-line errors inside the script). |
Comments suppressed due to low confidence (1)
crates/oxc_linter/src/lib.rs:616
- The comment says this trims the leading newline after
<script>, but the actual condition issource_text_offset() > 0, which will also apply to other partial sections (e.g. Astro frontmatter) and doesn’t specifically detect<script>tags. Consider rewording the comment to describe the real behavior/intent (trim implicit leading newline for partial sources to match ESLint BOF handling), so future readers don’t assume it’s Vue-only or tag-specific.
&self,
external_rules: &[(ExternalRuleId, ExternalOptionsId, AllowWarnDeny)],
path: &Path,
# Oxlint ### 💥 BREAKING CHANGES - 00ce512 oxlint/lsp: [**BREAKING**] Don't fix suggestions on fixAll code actions & command (#22195) (Sysix) ### 🚀 Features - 0eeceaf linter/no-unused-vars: Rename parameter with initializer (#22308) (camc314) - fa0232b linter/no-unused-vars: Add param rename suggestion (#22285) (Ryota Misumi) - ae59305 linter/promise/no-promise-in-callback: Add `exemptDeclarations` option (#22275) (Mikhail Baev) - 60bed4a linter: Extends `no-redundant-roles` and `prefer-tag-over-role` support roles (#22069) (mehm8128) - 545c80f linter/eslint: Implement `prefer-regex-literals` rule (#22192) (Mikhail Baev) - cf86d7a linter: Bulk suppression (#19328) (Said Atrahouch) - 23abd22 linter/jsx-a11y: Implement no-noninteractive-element-to-interactive-role (#21264) (Pedro Tainha) - fbb8f22 linter: Support `ignores` in overrides (#22148) (camc314) - 5a4414d oxlint/lsp: Support `rulesCustomization` lsp option (#21858) (Sysix) ### 🐛 Bug Fixes - 610f4c7 linter/no-unused-vars: Avoid renaming captured vars (#22310) (camc314) - 6b50f23 oxlint/cli: Load root config by searching up parent directories (#22272) (Sysix) - 31a5de7 linter: Rename override `ignores` to `excludeFiles` (#22283) (camc314) - 26d5d7b linter: Add missing vitest/valid-describe-callback functionality (#22279) (camchenry) - 784530f linter: `valid-title`: detect `String.raw` strings (#22271) (Sysix) - 080d90e linter: Move `no-debugger` fix to suggestion (#22256) (Sysix) - 25b7017 linter: Undocument override `ignores` option (#22213) (camc314) - 7bb00dd linter: Fix role-has-required-aria-props (#22097) (mehm8128) - d25279e linter/disable-directives: Improve parsing of names, descriptions (#22184) (camc314) - a59e447 linter/disable-directives: Ignore invalid enable suffixes (#22179) (camc314) - aafef0f ci: Disable bulk supression test on big endian (#22175) (camc314) - 281daec linter/vue/define-props-destructuring: Add `only-when-assigned` config opt (#22142) (camc314) - 46ab679 linter/plugins: Trim leading newline for partial sources (#20928) (bab) - 29ff6d9 linter: Update docs for no_alias_methods rule to be Vitest-specific and add toThrowError alias (#22129) (camchenry) ### ⚡ Performance - 9414bee linter/role-has-required-aria-props: Avoid intermediate vec (#22212) (camc314) - 3883ea3 linter/no-useless-escape: Drop unnecessary Vec collect (#22171) (connorshea) - 42c3029 linter/check-property-names: Replace split-collect-pop-join with rfind (#22172) (connorshea) - 9551d53 linter: Remove unnecessary Vec collect in CFG edge traversal (#22167) (connorshea) - 26fa2fc linter/aria-role: Remove unnecessary string allocations in run method (#22168) (connorshea) - c9ce045 linter/getter-return: Remove unnecessary Vec collect in CFG edge traversal (#22166) (connorshea) - 72bd846 linter/no-this-in-sfc: Reorder cheap name check, avoid String allocation (#22164) (connorshea) ### 📚 Documentation - 4da212a linter/no-unused-vars: Add docs to `rename_unused_function_parameter` (#22311) (camc314) - 27c4628 linter/forbid-dom-props: Escape jsx examples in lint rule docs (#22254) (4MBL) - 3f81147 linter: Improve the `react/jsx-key` rule docs. (#22162) (connorshea) - 07f03cc linter/consistent-return: Add note about `noImplicitReturns` coverage (#22156) (camc314) - 7c1e049 oxlint/lsp: Improve autogenerated lsp docs (#22154) (Sysix) - 87b3e38 linter: Update docs to be vitest-specific for consistent-test-it (#22128) (camchenry) # Oxfmt ### 💥 BREAKING CHANGES - 5c6c390 oxfmt: [**BREAKING**] Respect more git ignore options, align with Oxlint (#22210) (leaysgur) ### 🚀 Features - 6e8e818 oxfmt: Experimental .svelte support (#21700) (leaysgur) ### 🐛 Bug Fixes - e2a20b6 formatter: Add space after commas in import attributes (#22274) (Leonabcd123) ### ⚡ Performance - b756682 oxfmt: Optimize nested config prescan (#22232) (Jovi De Croock) - f14e81e formatter/sort_imports: Skip sort for single import runs (#22204) (leaysgur) - 32255b1 formatter: Process `ImportDeclaration`s in a run (#22079) (overlookmotel) ### 📚 Documentation - 4da6f4c formatter: Correct comment (#22217) (overlookmotel) - ef3507d formatter/sort_imports: Refresh docs (#22203) (leaysgur) Co-authored-by: Cameron <cameron.clark@hey.com>
fixes #20896
Tested manually with a repro project. Happy to add tests if there's a good way to test this path