Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,8 @@ issue](https://github.com/sds/overcommit/issues/238) for more details.
* [Mdl](lib/overcommit/hook/pre_commit/mdl.rb)
* [`*`MergeConflicts](lib/overcommit/hook/pre_commit/merge_conflicts.rb)
* [NginxTest](lib/overcommit/hook/pre_commit/nginx_test.rb)
* [OxFmt](lib/overcommit/hook/pre_commit/ox_fmt.rb)
* [OxLint](lib/overcommit/hook/pre_commit/ox_lint.rb)
* [PhpCs](lib/overcommit/hook/pre_commit/php_cs.rb)
* [PhpCsFixer](lib/overcommit/hook/pre_commit/php_cs_fixer.rb)
* [PhpLint](lib/overcommit/hook/pre_commit/php_lint.rb)
Expand Down
14 changes: 14 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,20 @@ PreCommit:
flags: ['-t']
include: '**/nginx.conf'

OxFmt:
enabled: false
description: 'Analyze with oxfmt'
required_executable: 'oxfmt'
flags: ['--check']
install_command: 'npm install -g oxfmt'

OxLint:
enabled: false
description: 'Analyze with oxlint'
required_executable: 'oxlint'
flags: ['--format=unix']
install_command: 'npm install -g oxlint'

Pep257: # Deprecated – use Pydocstyle instead.
enabled: false
description: 'Analyze docstrings with pep257'
Expand Down
35 changes: 35 additions & 0 deletions lib/overcommit/hook/pre_commit/ox_fmt.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

module Overcommit::Hook::PreCommit
# Runs `oxfmt` against any modified files.
#
# Protip: if you have an npm script set up to run oxfmt, you can configure
# this hook to run oxfmt via your npm script by using the `command` option in
# your .overcommit.yml file. This can be useful if you have some oxfmt
# configuration built into your npm script that you don't want to repeat
# somewhere else. Example:
#
# oxfmt:
# required_executable: 'npm'
# enabled: true
# command: ['npm', 'run', 'fmt', '--', '--check']
#
# Note: This hook only supports check mode.
#
# @see https://oxc.rs
class OxFmt < Base
def run
oxfmt_regex = /^(?<file>.+) \(\d+ms\)/
result = execute(command, args: applicable_files)
output = result.stdout.chomp
messages = output.split("\n").grep(oxfmt_regex)

return [:fail, result.stderr] if messages.empty? && !result.success?
return :pass if result.success? && output.empty?

# example message:
# test.js (5ms)
extract_messages(messages, oxfmt_regex)
end
end
end
35 changes: 35 additions & 0 deletions lib/overcommit/hook/pre_commit/ox_lint.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

module Overcommit::Hook::PreCommit
# Runs `oxlint` against any modified JavaScript files.
#
# Protip: if you have an npm script set up to run oxlint, you can configure
# this hook to run oxlint via your npm script by using the `command` option in
# your .overcommit.yml file. This can be useful if you have some oxlint
# configuration built into your npm script that you don't want to repeat
# somewhere else. Example:
#
# OxLint:
# required_executable: 'npm'
# enabled: true
# command: ['npm', 'run', 'lint', '--', '--format=unix']
#
# Note: This hook supports only unix format.
#
# @see https://oxc.rs
class OxLint < Base
def run
oxlint_regex = %r{^(?:file://)?(?<file>[^:]+):(?<line>\d+):\d+:.*?(?<type>Error|Warning)}
result = execute(command, args: applicable_files)
output = result.stdout.chomp
messages = output.split("\n").grep(oxlint_regex)

return [:fail, result.stderr] if messages.empty? && !result.success?
return :pass if result.success? && output.empty?

# example message:
# file://test.js:5:1: `debugger` statement is not allowed [Error/eslint(no-debugger)]
extract_messages(messages, oxlint_regex, lambda { |type| type.downcase.to_sym })
end
end
end
95 changes: 95 additions & 0 deletions spec/overcommit/hook/pre_commit/ox_fmt_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# frozen_string_literal: true

require 'spec_helper'

describe Overcommit::Hook::PreCommit::OxFmt do
let(:config) { Overcommit::ConfigurationLoader.default_configuration }
let(:context) { double('context') }
subject { described_class.new(config, context) }

before do
subject.stub(:applicable_files).and_return(%w[file1.js file2.js])
end

context 'when oxfmt is unable to run' do
let(:result) { double('result') }

before do
result.stub(:stderr).and_return('SyntaxError: Use of const in strict mode.')
result.stub(:stdout).and_return('')

result.stub(:success?).and_return(false)
subject.stub(:execute).and_return(result)
end

it { should fail_hook }
end

context 'when oxfmt exits successfully' do
let(:result) { double('result') }

before do
result.stub(:success?).and_return(true)
subject.stub(:execute).and_return(result)
end

context 'with no output' do
before do
result.stub(:stdout).and_return('')
end

it { should pass }
end

context 'and it reports an error' do
before do
result.stub(:stdout).and_return([
'Checking formatting...',
'',
'README.md (66ms)',
'',
'Format issues found in above 1 files. Run without `--check` to fix.',
'Finished in 66ms on 1 files using 8 threads.'
].join("\n"))
end

it { should fail_hook }
end

context 'and it doesnt count false positives error messages' do
before do
result.stub(:stdout).and_return([
'$ yarn oxfmt --check /app/project/Error.ts',
'$ /app/project/node_modules/.bin/oxfmt --check /app/project/Error.ts',
'',
].join("\n"))
end

it { should pass }
end
end

context 'when oxfmt exits unsucessfully' do
let(:result) { double('result') }

before do
result.stub(:success?).and_return(false)
subject.stub(:execute).and_return(result)
end

context 'and it reports an error' do
before do
result.stub(:stdout).and_return([
'Checking formatting...',
'',
'README.md (66ms)',
'',
'Format issues found in above 1 files. Run without `--check` to fix.',
'Finished in 66ms on 1 files using 8 threads.'
].join("\n"))
end

it { should fail_hook }
end
end
end
89 changes: 89 additions & 0 deletions spec/overcommit/hook/pre_commit/ox_lint_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# frozen_string_literal: true

require 'spec_helper'

describe Overcommit::Hook::PreCommit::OxLint do
let(:config) { Overcommit::ConfigurationLoader.default_configuration }
let(:context) { double('context') }
subject { described_class.new(config, context) }

before do
subject.stub(:applicable_files).and_return(%w[file1.js file2.js])
end

context 'when oxlint is unable to run' do
let(:result) { double('result') }

before do
result.stub(:stderr).and_return('SyntaxError: Use of const in strict mode.')
result.stub(:stdout).and_return('')

result.stub(:success?).and_return(false)
subject.stub(:execute).and_return(result)
end

it { should fail_hook }
end

context 'when oxlint exits successfully' do
let(:result) { double('result') }

before do
result.stub(:success?).and_return(true)
subject.stub(:execute).and_return(result)
end

context 'with no output' do
before do
result.stub(:stdout).and_return('')
end

it { should pass }
end

context 'and it reports a warning' do
before do
result.stub(:stdout).and_return([
'file://test.js:5:1: `debugger` statement is not allowed [Warning/eslint(no-debugger)]',
'',
'1 problem'
].join("\n"))
end

it { should warn }
end

context 'and it doesnt count false positives error messages' do
before do
result.stub(:stdout).and_return([
'$ yarn oxlint --quiet --format=unix /app/project/Error.ts',
'$ /app/project/node_modules/.bin/oxlint --quiet --format=compact /app/project/Error.ts',
'',
].join("\n"))
end

it { should pass }
end
end

context 'when oxlint exits unsucessfully' do
let(:result) { double('result') }

before do
result.stub(:success?).and_return(false)
subject.stub(:execute).and_return(result)
end

context 'and it reports an error' do
before do
result.stub(:stdout).and_return([
'file://test.js:5:1: `debugger` statement is not allowed [Error/eslint(no-debugger)]',
'',
'1 problem'
].join("\n"))
end

it { should fail_hook }
end
end
end
Loading