44
55use Illuminate \Console \Command ;
66use Illuminate \Support \Facades \File ;
7- use Illuminate \ Support \ Str ;
7+ use Symfony \ Component \ Console \ Terminal ;
88
99class InstallCommand extends Command
1010{
@@ -16,47 +16,192 @@ class InstallCommand extends Command
1616
1717 protected $ description = 'Installs the Tailwind CSS scaffolding for new Laravel applications. ' ;
1818
19+ private $ afterMessages = [];
20+
1921 public function handle ()
2022 {
21- $ this ->info ('Installing the Tailwind CSS scaffolding... ' );
23+ $ this ->displayHeader ('Installing TailwindCSS Laravel ' , ' <bg=blue;fg=black> INFO </> ' );
2224
23- $ this ->copyStubToAppIfMissing (
24- stub: __DIR__ . '/../../stubs/tailwind.config.js ' ,
25- to: base_path ('tailwind.config.js ' ),
26- );
25+ $ this ->ensureTailwindConfigExists ();
26+ $ this ->ensureTailwindCliBinaryExists ();
27+ $ this ->addImportStylesToLayouts ();
28+ $ this ->addIngoreLines ();
29+ $ this ->runFirstBuild ();
2730
28- $ this ->copyStubToAppIfMissing (
29- stub: __DIR__ . '/../../stubs/resources/css/app.css ' ,
30- to: resource_path ('css/app.css ' ),
31- );
31+ $ this ->displayAfterNotes ();
3232
33- if ($ this ->option ('download ' )) {
34- $ this ->call ('tailwindcss:download ' , [
35- '--cli-version ' => $ this ->option ('cli-version ' ) ?: config ('tailwindcss.version ' ),
36- ]);
37- } else {
38- $ this ->info ('Done! ' );
39- }
33+ $ this ->newLine ();
34+ $ this ->line (' <fg=white>Done!</> ' );
4035
4136 return self ::SUCCESS ;
4237 }
4338
39+ private function ensureTailwindConfigExists ()
40+ {
41+ $ this ->displayTask ('ensuring tailwind.config.js exists ' , function () {
42+ $ this ->copyStubToAppIfMissing (
43+ stub: __DIR__ . '/../../stubs/tailwind.config.js ' ,
44+ to: base_path ('tailwind.config.js ' ),
45+ );
46+
47+ $ this ->copyStubToAppIfMissing (
48+ stub: __DIR__ . '/../../stubs/resources/css/app.css ' ,
49+ to: resource_path ('css/app.css ' ),
50+ );
51+
52+ return self ::SUCCESS ;
53+ });
54+ }
55+
56+ private function ensureTailwindCliBinaryExists ()
57+ {
58+ if (! File::exists (config ('tailwindcss.bin_path ' )) || $ this ->option ('download ' )) {
59+ $ this ->displayTask ('downloading the Tailwind CLI binary ' , function () {
60+ return $ this ->callSilently ('tailwindcss:download ' , [
61+ '--cli-version ' => $ this ->option ('cli-version ' ) ?: config ('tailwindcss.version ' ),
62+ ]);
63+ });
64+ }
65+ }
66+
4467 private function copyStubToAppIfMissing (string $ stub , string $ to ): void
4568 {
4669 if (File::exists ($ to )) {
47- $ this ->warn (sprintf (" File %s already exists. " , $ this ->relativeOf ($ to )));
48-
4970 return ;
5071 }
5172
5273 File::ensureDirectoryExists (dirname ($ to ));
5374 File::copy ($ stub , $ to );
75+ }
5476
55- $ this ->info (sprintf (" Created the %s file. " , $ this ->relativeOf ($ to )));
77+ private function displayTask ($ description , $ task )
78+ {
79+ $ width = (new Terminal ())->getWidth ();
80+ $ dots = max (str_repeat ('<fg=gray>.</> ' , $ width - strlen ($ description ) - 13 ), 0 );
81+ $ this ->output ->write (sprintf (' <fg=white>%s</> %s ' , $ description , $ dots ));
82+ $ output = $ task ();
83+
84+ if ($ output === self ::SUCCESS ) {
85+ $ this ->output ->write ('<info>DONE</info> ' );
86+ } elseif ($ output === self ::FAILURE ) {
87+ $ this ->output ->write ('<error>FAIL</error> ' );
88+ } elseif ($ output === self ::INVALID ) {
89+ $ this ->output ->write ('<fg=yellow>WARN</> ' );
90+ }
91+
92+ $ this ->newLine ();
93+ }
94+
95+ private function displayHeader ($ text , $ prefix )
96+ {
97+ $ this ->newLine ();
98+ $ this ->line (sprintf (' %s <fg=white>%s</> ' , $ prefix , $ text ));
99+ $ this ->newLine ();
56100 }
57101
58- private function relativeOf ( string $ path ): string
102+ private function addImportStylesToLayouts ()
59103 {
60- return Str::after ($ path , rtrim (base_path (), '/ ' ) . '/ ' );
104+ $ this ->displayTask ('updating layouts ' , function () {
105+ if (File::exists (base_path ('webpack.mix.js ' ))) {
106+ $ this ->replaceMixStylesToLayouts ();
107+ } elseif (File::exists (base_path ('vite.config.js ' ))) {
108+ $ this ->replaceViteStylesToLayouts ();
109+ } else {
110+ $ this ->appendTailwindStylesToLayouts ();
111+ }
112+
113+ return self ::SUCCESS ;
114+ });
115+ }
116+
117+ private function replaceMixStylesToLayouts ()
118+ {
119+ $ this ->existingLayouts ()
120+ ->each (fn ($ file ) => File::put (
121+ $ file ,
122+ str_replace (
123+ "mix('css/app.css') " ,
124+ "tailwindcss('css/app.css') " ,
125+ File::get ($ file ),
126+ ),
127+ ));
128+ }
129+
130+ private function replaceViteStylesToLayouts ()
131+ {
132+ $ this ->existingLayouts ()
133+ ->each (fn ($ file ) => File::put (
134+ $ file ,
135+ preg_replace (
136+ '/\@vite\(\[ \'resources\/css\/app.css \', \'resources\/js\/app.js \'\]\)/ ' ,
137+ "@vite(['resources/js/app.js']) " ,
138+ File::get ($ file ),
139+ ),
140+ ));
141+
142+ $ this ->appendTailwindStylesToLayouts ();
143+ }
144+
145+ private function appendTailwindStylesToLayouts ()
146+ {
147+ $ this ->existingLayoutFiles ()
148+ ->each (fn ($ file ) => File::put (
149+ $ file ,
150+ preg_replace (
151+ '/(\s*)(<\/head>)/ ' ,
152+ "\\1 <link rel= \"stylesheet \" href= \"{{ tailwindcss('css/app.css') }} \"> \n\\1 \\2 " ,
153+ File::get ($ file ),
154+ ),
155+ ));
156+ }
157+
158+ private function existingLayoutFiles ()
159+ {
160+ return collect (['app ' , 'guest ' ])
161+ ->map (fn ($ file ) => resource_path ("views/layouts/ {$ file }.blade.php " ))
162+ ->filter (fn ($ file ) => File::exists ($ file ));
163+ }
164+
165+ private function addIngoreLines ()
166+ {
167+ $ this ->displayTask ('adding ignore lines ' , function () {
168+ $ binary = basename (config ('tailwindcss.bin_path ' ));
169+
170+ File::append (base_path ('.gitignore ' ), <<<LINES
171+
172+ /public/css/
173+ /public/dist/
174+ .tailwindcss-manifest.json
175+ {$ binary }
176+ LINES );
177+
178+ return self ::SUCCESS ;
179+ });
180+ }
181+
182+ private function runFirstBuild ()
183+ {
184+ $ this ->displayTask ('running first build ' , function () {
185+ $ result = $ this ->callSilently ('tailwindcss:build ' );
186+
187+ if ($ result !== self ::SUCCESS ) {
188+ $ this ->afterMessages [] = '<fg=white>* Try running `<fg=yellow>php artisan tailwindcss:build</>`</> ' ;
189+
190+ return self ::INVALID ;
191+ }
192+
193+ return self ::SUCCESS ;
194+ });
195+ }
196+
197+ private function displayAfterNotes ()
198+ {
199+ if (count ($ this ->afterMessages ) > 0 ) {
200+ $ this ->displayHeader ('After Notes & Next Steps ' , '<bg=yellow;fg=black> NOTES </> ' );
201+
202+ foreach ($ this ->afterMessages as $ message ) {
203+ $ this ->line (' ' .$ message );
204+ }
205+ }
61206 }
62207}
0 commit comments