Skip to content

Commit 35b5e02

Browse files
authored
Merge pull request #3029 from Parsely/add/simple-linter
2 parents 92ca008 + f26ffa0 commit 35b5e02

File tree

18 files changed

+187
-21
lines changed

18 files changed

+187
-21
lines changed

.husky/pre-commit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
php lint.php
12
npm run lint
23
composer cs
34
vendor/bin/phpstan analyse --memory-limit=1G

CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
281281

282282
- Initialize Parse.ly option before registering the setting ([#2001](https://github.com/Parsely/wp-parsely/pull/2001))
283283

284-
285284
## [3.11.0](https://github.com/Parsely/wp-parsely/compare/3.10.0...3.11.0) - 2023-11-13
286285

287286
### Added

lint.php

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
<?php
2+
/**
3+
* Small linter that can detect and fix some code style violations
4+
*
5+
* Usage:
6+
* php lint.php // Show errors.
7+
* php lint.php --fix // Auto-fix errors where supported.
8+
*
9+
* @since 3.18.0
10+
*/
11+
12+
// Configuration.
13+
$config = array(
14+
new Lint_Rule(
15+
'Two or more consecutive empty lines found',
16+
'/(\r?\n){3,}/',
17+
'/\.(php|js|css|html|scss|yml|md|ts|tsx|sh|json)$/',
18+
"\n\n"
19+
),
20+
new Lint_Rule(
21+
'One or more empty lines between closing brackets found',
22+
'/(\})\n{2,}(\})/',
23+
'/\.(php|js|css|html|scss|yml|md|ts|tsx|sh|json)$/',
24+
"$1\n$2"
25+
),
26+
);
27+
28+
// Run the rules and display any errors.
29+
foreach ( $config as $rule ) {
30+
$errors = $rule->run( in_array( '--fix', $argv ) );
31+
32+
if ( '' !== $errors ) {
33+
$exit_code = 1;
34+
echo $errors;
35+
}
36+
}
37+
38+
exit( $exit_code ?? 0 );
39+
40+
/**
41+
* A class representing a linting rule.
42+
*
43+
* @since 3.18.0
44+
*/
45+
class Lint_Rule {
46+
/**
47+
* @var string Error message to display when this linting rule is violated.
48+
*/
49+
private $error_message;
50+
51+
/**
52+
* @var string Regex pattern used for rule violation detection.
53+
*/
54+
private $error_pattern;
55+
56+
/**
57+
* @var string Regex pattern used to specify the files to search in.
58+
*/
59+
private $include_files;
60+
61+
/**
62+
* @var string|null Optional regex pattern used to auto-fix the violation.
63+
*/
64+
private $fix_pattern;
65+
66+
/**
67+
* @var array<string> Directories to exclude from search.
68+
*/
69+
private $exclude_dirs;
70+
71+
/**
72+
* @var array<string> Array of errors found when running the rule.
73+
*/
74+
private $errors = array();
75+
76+
/**
77+
* Constructor.
78+
*
79+
* @since 3.18.0
80+
*
81+
* @param string $error_message The error message to display.
82+
* @param string $error_pattern The regex matching the rule violation.
83+
* @param string $include_files The regex matching the files to search in.
84+
* @param string|null $fix_pattern The regex to fix any violations (optional).
85+
* @param array<string> $exclude_dirs The directories to exclude from search.
86+
*/
87+
public function __construct(
88+
string $error_message,
89+
string $error_pattern,
90+
string $include_files,
91+
string $fix_pattern = null,
92+
array $exclude_dirs = array( 'artifacts', 'build', 'vendor', 'node_modules' )
93+
) {
94+
$this->error_message = $error_message;
95+
$this->error_pattern = $error_pattern;
96+
$this->include_files = $include_files;
97+
$this->fix_pattern = $fix_pattern;
98+
$this->exclude_dirs = $exclude_dirs;
99+
}
100+
101+
/**
102+
* Runs the rule to detect or auto-fix any violations.
103+
*
104+
* @since 3.18.0
105+
*
106+
* @param bool $auto_fix_mode Whether violations should be auto-fixed.
107+
* @return string The violations found, or an empty string.
108+
*/
109+
public function run( $auto_fix_mode ): string {
110+
$base_dir = __DIR__ . '/';
111+
$iterator = $this->create_iterator( $this->exclude_dirs );
112+
113+
// Iterate over the directories and files.
114+
foreach ( $iterator as $file ) {
115+
// Check if the item is a file that should be checked.
116+
if ( $file->isFile() && preg_match( $this->include_files, $file->getFilename() ) ) {
117+
$file_content = file_get_contents( $file->getPathname() );
118+
119+
// Check for rule violations.
120+
if ( preg_match_all( $this->error_pattern, $file_content, $matches, PREG_OFFSET_CAPTURE ) ) {
121+
$relative_path = str_replace( $base_dir, '', $file->getPathname() );
122+
123+
// Auto-fix or log the issue.
124+
if ( $auto_fix_mode && null !== $this->fix_pattern ) {
125+
$fixed_content = preg_replace( $this->error_pattern, $this->fix_pattern, $file_content );
126+
127+
if ( null === $fixed_content ) {
128+
$this->errors[] = "Error while trying to lint {$relative_path}.";
129+
}
130+
131+
if ( false === file_put_contents( $file->getPathname(), $fixed_content ) ) {
132+
$this->errors[] = "Failed to save fix for {$relative_path}.";
133+
}
134+
} else {
135+
foreach ( $matches[0] as $match ) {
136+
$line_number = substr_count( substr( $file_content, 0, $match[1] ), "\n" ) + 1;
137+
$this->errors[] = "{$relative_path} \033[90m(line {$line_number})\033[0m";
138+
}
139+
}
140+
}
141+
}
142+
}
143+
144+
if ( empty( $this->errors ) ) {
145+
return '';
146+
}
147+
148+
$output = "\033[31mERROR: {$this->error_message}.\033[0m\n";
149+
if ( null !== $this->fix_pattern ) {
150+
$output .= "\033[33mAuto-fixable with the --fix parameter.\033[0m\n";
151+
}
152+
$output .= "\033[36mYou can use the {$this->error_pattern} regex to find the offending code.\033[0m\n\n";
153+
154+
foreach ( $this->errors as $error ) {
155+
$output .= " - {$error}\n";
156+
}
157+
158+
return $output . "\n\n";
159+
}
160+
161+
/**
162+
* Creates an iterator that iterates across the project's folder structure,
163+
* skipping excluded directories.
164+
*
165+
* @since 3.18.0
166+
*
167+
* @return RecursiveIteratorIterator
168+
*/
169+
private function create_iterator(): RecursiveIteratorIterator {
170+
return new RecursiveIteratorIterator(
171+
new RecursiveCallbackFilterIterator(
172+
new RecursiveDirectoryIterator( __DIR__, RecursiveDirectoryIterator::SKIP_DOTS ),
173+
function ( $current, $key, $iterator ) {
174+
if ( $iterator->hasChildren() && in_array( $current->getFilename(), $this->exclude_dirs ) ) {
175+
return false; // Skip excluded directories.
176+
}
177+
178+
return true;
179+
}
180+
),
181+
RecursiveIteratorIterator::SELF_FIRST
182+
);
183+
}
184+
}
185+
186+
// phpcs:ignoreFile

src/@types/assets/window.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,4 @@ declare global {
5353
};
5454
};
5555
}
56-
5756
}

src/Telemetry/Tracks/class-tracks-pixel.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
use Parsely\Utils\Utils;
1414
use WP_Error;
1515

16-
1716
/**
1817
* Handles all operations related to the Tracks pixel.
1918
*

src/content-helper/common/class-content-helper-feature.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ abstract public static function get_style_id(): string;
7373
*/
7474
abstract public function run(): void;
7575

76-
7776
/**
7877
* Examines filters and conditions to determine whether the feature can be
7978
* enabled.

src/content-helper/common/components/input-range/style.scss

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,4 @@
5353
}
5454
}
5555
}
56-
57-
5856
}

src/content-helper/editor-sidebar/performance-stats/performance-stats.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,6 @@
143143
}
144144
}
145145

146-
147146
// Multi-percentage bar.
148147
div.multi-percentage-bar {
149148
position: relative;

src/content-helper/editor-sidebar/related-posts/related-posts.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@
125125
}
126126
}
127127

128-
129128
.related-posts-list {
130129
display: flex;
131130
padding: to_rem(6px) 0 var(--grid-unit-20) 0;

src/rest-api/content-helper/class-endpoint-smart-linking.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,6 @@ public function get_smart_links( WP_REST_Request $request ): WP_REST_Response {
276276
return new WP_REST_Response( array( 'data' => $response ), 200 );
277277
}
278278

279-
280279
/**
281280
* API Endpoint: POST /smart-linking/{post_id}/add.
282281
*
@@ -475,7 +474,6 @@ public function set_smart_links( WP_REST_Request $request ): WP_REST_Response {
475474
return new WP_REST_Response( array( 'data' => $response ), 200 );
476475
}
477476

478-
479477
/**
480478
* API Endpoint: POST /smart-linking/url-to-post-type.
481479
*

0 commit comments

Comments
 (0)