6
6
7
7
use Nette \Utils \FileSystem ;
8
8
use Rector \Console \Command \CustomRuleCommand ;
9
- use Symfony \Component \Finder \Finder ;
10
- use Symfony \Component \Finder \SplFileInfo ;
11
9
12
10
/**
11
+ * Generates scaffolding for a new Rector rule
12
+ *
13
13
* Modified version of
14
14
*
15
15
* @see CustomRuleCommand
16
16
*/
17
17
final class MakeRuleCommand
18
18
{
19
- public function execute (string $ ruleName ): int
19
+ private const string TEMPLATE_DIR = __DIR__ . '/../templates ' ;
20
+
21
+ private bool $ configurable = false ;
22
+ private ?string $ directory = null ;
23
+ private string $ rulesNamespace = 'RectorLaravel \\Rector ' ;
24
+ private string $ testsNamespace = 'RectorLaravel \\Tests \\Rector ' ;
25
+ private string $ testDir = 'tests/Rector/ ' ;
26
+ private string $ currentDirectory ;
27
+
28
+ public function execute (string $ ruleName , bool $ configurable = false ): int
20
29
{
21
- $ rectorName = $ this ->getRuleName ($ ruleName );
22
- $ rulesNamespace = 'RectorLaravel \\Rector ' ;
23
- $ testsNamespace = 'RectorLaravel \\Tests \\Rector ' ;
30
+ $ this ->configurable = $ configurable ;
24
31
25
- // find all files in templates directory
26
- $ finder = Finder::create ()
27
- ->files ()
28
- ->in (__DIR__ . '/../templates/new-rule ' )
29
- ->notName ('__NAME__Test.php ' );
32
+ // Validate rule name
33
+ if (empty ($ ruleName )) {
34
+ echo PHP_EOL . 'Rule name must not be empty. ' . PHP_EOL ;
35
+
36
+ return 1 ;
37
+ }
30
38
31
- $ finder ->append ([
32
- new SplFileInfo (
33
- __DIR__ . '/../templates/new-rule/tests/Rector/__NAME__/__NAME__Test.php ' ,
34
- 'tests/Rector/__NAME__ ' ,
35
- 'tests/Rector/__NAME__/__NAME__Test.php ' ,
36
- ),
37
- ]);
39
+ $ this ->currentDirectory = (string ) getcwd ();
38
40
39
- $ currentDirectory = getcwd ( );
41
+ $ rectorName = $ this -> setNamespacesAndDirectories ( $ ruleName );
40
42
41
- $ generatedFilePaths = [];
43
+ $ generatedFilePaths = [
44
+ $ this ->createRule ($ rectorName ),
45
+ $ this ->createTest ($ rectorName ),
46
+ $ this ->createTestFixture ($ rectorName ),
47
+ $ this ->createTestConfig ($ rectorName ),
48
+ ];
42
49
43
- $ fileInfos = iterator_to_array ($ finder ->getIterator ());
50
+ echo 'Generated files: ' . PHP_EOL . PHP_EOL . "\t" ;
51
+ echo implode (PHP_EOL . "\t" , array_filter ($ generatedFilePaths )) . PHP_EOL ;
44
52
45
- foreach ($ fileInfos as $ fileInfo ) {
46
- $ newContent = $ this ->replaceNameVariable ($ rectorName , $ fileInfo ->getContents ());
47
- $ newContent = $ this ->replaceNamespaceVariable ($ rulesNamespace , $ newContent );
48
- $ newContent = $ this ->replaceTestsNamespaceVariable ($ testsNamespace , $ newContent );
49
- $ newFilePath = $ this ->replaceNameVariable ($ rectorName , $ fileInfo ->getRelativePathname ());
53
+ return 0 ;
54
+ }
50
55
51
- FileSystem::write ($ currentDirectory . '/ ' . $ newFilePath , $ newContent , null );
56
+ private function setNamespacesAndDirectories (string $ ruleName ): string
57
+ {
58
+ // Extract directory and rule name if format contains slashes like "Directory/SubDir/More/RuleName"
59
+ if (str_contains ($ ruleName , '/ ' )) {
60
+ $ parts = explode ('/ ' , $ ruleName );
61
+ // The last part is the rule name
62
+ $ ruleName = array_pop ($ parts );
63
+ // All other parts form the directory path
64
+ if (! empty ($ parts )) {
65
+ $ this ->directory = implode ('/ ' , $ parts ) . '/ ' ;
66
+ }
67
+ }
52
68
53
- $ generatedFilePaths [] = $ newFilePath ;
69
+ // Add directory to namespace if provided
70
+ if ($ this ->directory !== null ) {
71
+ // Clean directory name (ensure it ends with a backslash for paths but not for namespace)
72
+ $ cleanDir = rtrim ($ this ->directory , '\\/ ' );
73
+ $ this ->directory = $ cleanDir . '/ ' ;
74
+ $ namespaceDir = str_replace ('/ ' , '\\' , $ cleanDir );
75
+ $ this ->rulesNamespace .= '\\' . $ namespaceDir ;
76
+ $ this ->testsNamespace .= '\\' . $ namespaceDir ;
54
77
}
55
78
56
- echo PHP_EOL . 'Generated files: ' . PHP_EOL . PHP_EOL . "\t" ;
57
- echo implode (PHP_EOL . "\t" , $ generatedFilePaths ) . PHP_EOL ;
79
+ $ rectorName = $ this ->formatRuleName ($ ruleName );
58
80
59
- return 0 ;
81
+ $ this ->testDir .= $ this ->directory . $ rectorName ;
82
+
83
+ return $ rectorName ;
84
+ }
85
+
86
+ private function createRule (string $ rectorName ): ?string
87
+ {
88
+ $ ruleFilePath = null ;
89
+ $ ruleTemplateFile = $ this ->configurable
90
+ ? self ::TEMPLATE_DIR . '/configurable-rule.php.template '
91
+ : self ::TEMPLATE_DIR . '/non-configurable-rule.php.template ' ;
92
+
93
+ if (file_exists ($ ruleTemplateFile )) {
94
+ $ contents = file_get_contents ($ ruleTemplateFile );
95
+
96
+ $ newContent = $ this ->replaceNameVariable ($ rectorName , $ contents );
97
+ $ newContent = $ this ->replaceNamespaceVariable ($ newContent );
98
+ $ newContent = $ this ->replaceTestsNamespaceVariable ($ newContent );
99
+
100
+ // Create the rule file path
101
+ $ ruleFilePath = 'src/Rector/ ' ;
102
+ if ($ this ->directory !== null ) {
103
+ $ ruleFilePath .= $ this ->directory ;
104
+ }
105
+ $ ruleFilePath .= $ rectorName . '.php ' ;
106
+
107
+ // Ensure directory exists
108
+ $ this ->ensureDirectoryExists ($ this ->currentDirectory . '/ ' . dirname ($ ruleFilePath ));
109
+ FileSystem::write ($ this ->currentDirectory . '/ ' . $ ruleFilePath , $ newContent , null );
110
+ }
111
+
112
+ return $ ruleFilePath ;
113
+ }
114
+
115
+ private function createTest (string $ rectorName ): ?string
116
+ {
117
+ $ testFilePath = null ;
118
+ $ testTemplateFile = self ::TEMPLATE_DIR . '/test.php.template ' ;
119
+
120
+ if (file_exists ($ testTemplateFile )) {
121
+ $ contents = file_get_contents ($ testTemplateFile );
122
+ $ newContent = $ this ->replaceNameVariable ($ rectorName , $ contents );
123
+ $ newContent = $ this ->replaceNamespaceVariable ($ newContent );
124
+ $ newContent = $ this ->replaceTestsNamespaceVariable ($ newContent );
125
+
126
+ $ testFilePath = $ this ->testDir . '/ ' . $ rectorName . 'Test.php ' ;
127
+ $ this ->ensureDirectoryExists ($ this ->currentDirectory . '/ ' . dirname ($ testFilePath ));
128
+ FileSystem::write ($ this ->currentDirectory . '/ ' . $ testFilePath , $ newContent , null );
129
+ }
130
+
131
+ return $ testFilePath ;
132
+ }
133
+
134
+ private function createTestFixture (string $ rectorName ): ?string
135
+ {
136
+ $ fixtureFilePath = null ;
137
+ $ fixtureDir = $ this ->testDir . '/Fixture ' ;
138
+ $ this ->ensureDirectoryExists ($ this ->currentDirectory . '/ ' . $ fixtureDir );
139
+
140
+ // Create fixture file from template
141
+ $ fixtureTemplateFile = self ::TEMPLATE_DIR . '/fixture.php.inc.template ' ;
142
+
143
+ if (file_exists ($ fixtureTemplateFile )) {
144
+ $ fixtureContents = file_get_contents ($ fixtureTemplateFile );
145
+ $ fixtureContents = $ this ->replaceNameVariable ($ rectorName , $ fixtureContents );
146
+ $ fixtureContents = $ this ->replaceTestsNamespaceVariable ($ fixtureContents );
147
+
148
+ $ fixtureFilePath = $ fixtureDir . '/some_class.php.inc ' ;
149
+ FileSystem::write ($ this ->currentDirectory . '/ ' . $ fixtureFilePath , $ fixtureContents , null );
150
+ }
151
+
152
+ return $ fixtureFilePath ;
60
153
}
61
154
62
- private function getRuleName (string $ ruleName ): string
155
+ private function createTestConfig (string $ rectorName ): ?string
156
+ {
157
+ $ configFilePath = null ;
158
+ $ configDir = $ this ->testDir . '/config ' ;
159
+ $ this ->ensureDirectoryExists ($ this ->currentDirectory . '/ ' . $ configDir );
160
+
161
+ // Create config file from template - select based on configurability
162
+ $ configTemplateFile = $ this ->configurable
163
+ ? self ::TEMPLATE_DIR . '/configurable-config.php.template '
164
+ : self ::TEMPLATE_DIR . '/non-configurable-config.php.template ' ;
165
+
166
+ if (file_exists ($ configTemplateFile )) {
167
+ $ configContents = file_get_contents ($ configTemplateFile );
168
+ $ configContents = $ this ->replaceNameVariable ($ rectorName , $ configContents );
169
+ $ configContents = $ this ->replaceNamespaceVariable ($ configContents );
170
+
171
+ $ configFilePath = $ configDir . '/configured_rule.php ' ;
172
+ FileSystem::write ($ this ->currentDirectory . '/ ' . $ configFilePath , $ configContents , null );
173
+ }
174
+
175
+ return $ configFilePath ;
176
+ }
177
+
178
+ private function formatRuleName (string $ ruleName ): string
63
179
{
64
180
if (! str_ends_with ($ ruleName , 'Rector ' )) {
65
181
$ ruleName .= 'Rector ' ;
@@ -73,13 +189,20 @@ private function replaceNameVariable(string $rectorName, string $contents): stri
73
189
return str_replace ('__NAME__ ' , $ rectorName , $ contents );
74
190
}
75
191
76
- private function replaceNamespaceVariable (string $ namespace , string $ contents ): string
192
+ private function replaceNamespaceVariable (string $ contents ): string
77
193
{
78
- return str_replace ('__NAMESPACE__ ' , $ namespace , $ contents );
194
+ return str_replace ('__NAMESPACE__ ' , $ this -> rulesNamespace , $ contents );
79
195
}
80
196
81
- private function replaceTestsNamespaceVariable (string $ testsNamespace , string $ contents ): string
197
+ private function replaceTestsNamespaceVariable (string $ contents ): string
82
198
{
83
- return str_replace ('__TESTS_NAMESPACE__ ' , $ testsNamespace , $ contents );
199
+ return str_replace ('__TESTS_NAMESPACE__ ' , $ this ->testsNamespace , $ contents );
200
+ }
201
+
202
+ private function ensureDirectoryExists (string $ directory ): void
203
+ {
204
+ if (! is_dir ($ directory )) {
205
+ mkdir ($ directory , 0777 , true );
206
+ }
84
207
}
85
208
}
0 commit comments