33namespace Tonysm \ImportmapLaravel \Commands ;
44
55use Illuminate \Console \Command ;
6- use Illuminate \Support \Facades \Artisan ;
7- use Illuminate \Support \Facades \Event ;
86use Illuminate \Support \Facades \File ;
7+ use Illuminate \Support \Facades \Process ;
98use Illuminate \Support \Str ;
109use Symfony \Component \Console \Attribute \AsCommand ;
11- use Symfony \Component \Console \ Terminal ;
10+ use Symfony \Component \Process \ PhpExecutableFinder ;
1211use Tonysm \ImportmapLaravel \Actions \FixJsImportPaths ;
1312use Tonysm \ImportmapLaravel \Actions \ReplaceOrAppendTags ;
14- use Tonysm \ImportmapLaravel \Events \FailedToFixImportStatement ;
1513
1614#[AsCommand('importmap:install ' )]
1715class InstallCommand extends Command
@@ -20,12 +18,8 @@ class InstallCommand extends Command
2018
2119 public $ description = 'Installs the package. ' ;
2220
23- public $ afterMessages = [];
24-
2521 public function handle (): int
2622 {
27- $ this ->displayHeader ('Installing Importmap Laravel ' , '<bg=blue;fg=black> INFO </> ' );
28-
2923 File::ensureDirectoryExists (resource_path ('js ' ));
3024
3125 $ this ->convertLocalImportsFromUsingDots ();
@@ -34,112 +28,83 @@ public function handle(): int
3428 $ this ->updateAppLayouts ();
3529 $ this ->deleteNpmRelatedFiles ();
3630 $ this ->configureIgnoredFolder ();
37-
38- $ this ->displayAfterNotes ();
31+ $ this ->runStorageLinkCommand ();
3932
4033 $ this ->newLine ();
41- $ this ->line ( ' <fg=white>Done!</> ' );
34+ $ this ->components -> info ( ' Importmap Laravel was installed succesfully. ' );
4235
4336 return self ::SUCCESS ;
4437 }
4538
4639 private function deleteNpmRelatedFiles (): void
4740 {
48- $ this ->displayTask ('removing NPM related files ' , function () {
49- $ files = [
50- 'package.json ' ,
51- 'package-lock.json ' ,
52- 'webpack.mix.js ' ,
53- 'postcss.config.js ' ,
54- 'vite.config.js ' ,
55- ];
56-
57- collect ($ files )
58- ->map (fn ($ file ) => base_path ($ file ))
59- ->filter (fn ($ file ) => File::exists ($ file ))
60- ->each (fn ($ file ) => File::delete ($ file ));
61-
62- return self ::SUCCESS ;
63- });
41+ $ files = [
42+ 'package.json ' ,
43+ 'package-lock.json ' ,
44+ 'webpack.mix.js ' ,
45+ 'postcss.config.js ' ,
46+ 'vite.config.js ' ,
47+ ];
48+
49+ collect ($ files )
50+ ->map (fn ($ file ) => base_path ($ file ))
51+ ->filter (fn ($ file ) => File::exists ($ file ))
52+ ->each (fn ($ file ) => File::delete ($ file ));
6453 }
6554
6655 private function publishImportmapFiles (): void
6756 {
68- $ this ->displayTask ('publishing the `routes/importmap.php` file ' , function () {
69- File::copy (dirname (__DIR__ , 2 ).implode (DIRECTORY_SEPARATOR , ['' , 'stubs ' , 'routes ' , 'importmap.php ' ]), base_path (implode (DIRECTORY_SEPARATOR , ['routes ' , 'importmap.php ' ])));
70- File::copy (dirname (__DIR__ , 2 ).implode (DIRECTORY_SEPARATOR , ['' , 'stubs ' , 'jsconfig.json ' ]), base_path ('jsconfig.json ' ));
71-
72- return self ::SUCCESS ;
73- });
57+ File::copy (dirname (__DIR__ , 2 ).implode (DIRECTORY_SEPARATOR , ['' , 'stubs ' , 'routes ' , 'importmap.php ' ]), base_path (implode (DIRECTORY_SEPARATOR , ['routes ' , 'importmap.php ' ])));
58+ File::copy (dirname (__DIR__ , 2 ).implode (DIRECTORY_SEPARATOR , ['' , 'stubs ' , 'jsconfig.json ' ]), base_path ('jsconfig.json ' ));
7459 }
7560
7661 private function convertLocalImportsFromUsingDots (): void
7762 {
78- Event::listen (function (FailedToFixImportStatement $ event ) {
79- $ this ->afterMessages [] = sprintf (
80- 'Failed to fix import statement (%s) in file (%). ' ,
81- $ event ->importStatement ,
82- str_replace (base_path (), '' , $ event ->file ->getPath ()),
83- );
84- });
85-
86- $ this ->displayTask ('converting js imports ' , function () {
87- $ root = rtrim (resource_path ('js ' ), DIRECTORY_SEPARATOR ).DIRECTORY_SEPARATOR ;
88-
89- (new FixJsImportPaths ($ root ))();
90-
91- return self ::SUCCESS ;
92- });
63+ (new FixJsImportPaths (rtrim (resource_path ('js ' ), DIRECTORY_SEPARATOR ).DIRECTORY_SEPARATOR ))();
9364 }
9465
9566 private function importDependenciesFromNpm (): void
9667 {
97- $ this ->displayTask ('pinning dependencies from NPM ' , function () {
98- if (! File::exists ($ packageJsonFile = base_path ('package.json ' ))) {
99- $ this ->afterMessages [] = '<fg=white>* Pinning was skipped because of missing package.json</> ' ;
100-
101- return self ::INVALID ;
102- }
103-
104- $ this ->afterMessages [] = '<fg=white>* Some dev dependencies could \'ve been skipped...</> ' ;
68+ if (! File::exists ($ packageJsonFile = base_path ('package.json ' ))) {
69+ return ;
70+ }
10571
106- $ filteredOutDependencies = [
107- '@tailwindcss/forms ' ,
108- '@tailwindcss/typography ' ,
109- 'autoprefixer ' ,
110- 'laravel-vite-plugin ' ,
111- 'postcss ' ,
112- 'tailwindcss ' ,
113- 'vite ' ,
114- ];
72+ $ filteredOutDependencies = [
73+ '@tailwindcss/forms ' ,
74+ '@tailwindcss/typography ' ,
75+ 'autoprefixer ' ,
76+ 'laravel-vite-plugin ' ,
77+ 'postcss ' ,
78+ 'tailwindcss ' ,
79+ 'vite ' ,
80+ ];
11581
116- $ packageJson = json_decode (File::get ($ packageJsonFile ), true );
82+ $ packageJson = json_decode (File::get ($ packageJsonFile ), true );
11783
118- $ dependencies = collect (array_replace ($ packageJson ['dependencies ' ] ?? [], $ packageJson ['devDependencies ' ] ?? []))
119- ->filter (fn ($ _version , $ package ) => ! in_array ($ package , $ filteredOutDependencies ))
120- // Axios has an issue with importmaps, so we'll hardcode the version for now...
121- ->map (fn ($ version , $ package ) => $ package === 'axios ' ? 'axios@0.27 ' : "\"{$ package }@ {$ version }\"" )
122- ->join (' ' );
84+ $ dependencies = collect (array_replace ($ packageJson ['dependencies ' ] ?? [], $ packageJson ['devDependencies ' ] ?? []))
85+ ->filter (fn ($ _version , $ package ) => ! in_array ($ package , $ filteredOutDependencies ))
86+ // Axios has an issue with importmaps, so we'll hardcode the version for now...
87+ ->map (fn ($ version , $ package ) => $ package === 'axios ' ? 'axios@0.27 ' : "\"{$ package }@ {$ version }\"" );
12388
124- if (trim ($ dependencies ) === '' ) {
125- return self :: SUCCESS ;
126- }
89+ if (trim ($ dependencies-> join ( '' ) ) === '' ) {
90+ return ;
91+ }
12792
128- return Artisan::call ("importmap:pin {$ dependencies }" );
93+ Process::forever ()->run (array_merge ([
94+ $ this ->phpBinary (),
95+ 'artisan ' ,
96+ 'importmap:pin ' ,
97+ ], $ dependencies ->all ()), function ($ _type , $ output ) {
98+ $ this ->output ->write ($ output );
12999 });
130100 }
131101
132102 private function updateAppLayouts (): void
133103 {
134- $ this ->displayTask ('Updating layout files ' , function () {
135- $ this ->existingLayoutFiles ()
136- ->each (fn ($ file ) => File::put (
137- $ file ,
138- (new ReplaceOrAppendTags ())(File::get ($ file )),
139- ));
140-
141- return self ::SUCCESS ;
142- });
104+ $ this ->existingLayoutFiles ()->each (fn ($ file ) => File::put (
105+ $ file ,
106+ (new ReplaceOrAppendTags ())(File::get ($ file )),
107+ ));
143108 }
144109
145110 private function existingLayoutFiles ()
@@ -149,55 +114,53 @@ private function existingLayoutFiles()
149114 ->filter (fn ($ file ) => File::exists ($ file ));
150115 }
151116
152- private function displayHeader ($ text , $ prefix )
153- {
154- $ this ->newLine ();
155- $ this ->line (sprintf (' %s <fg=white>%s</> ' , $ prefix , $ text ));
156- $ this ->newLine ();
157- }
158-
159- private function displayTask ($ description , $ task )
160- {
161- $ width = (new Terminal ())->getWidth ();
162- $ dots = max (str_repeat ('<fg=gray>.</> ' , $ width - strlen ($ description ) - 13 ), 0 );
163- $ this ->output ->write (sprintf (' <fg=white>%s</> %s ' , $ description , $ dots ));
164- $ output = $ task ();
165-
166- if ($ output === self ::SUCCESS ) {
167- $ this ->output ->write ('<info>DONE</info> ' );
168- } elseif ($ output === self ::FAILURE ) {
169- $ this ->output ->write ('<error>FAIL</error> ' );
170- } elseif ($ output === self ::INVALID ) {
171- $ this ->output ->write ('<fg=yellow>WARN</> ' );
172- }
173-
174- $ this ->newLine ();
175- }
176-
177117 private function configureIgnoredFolder ()
178118 {
179119 if (Str::contains (File::get (base_path ('.gitignore ' )), 'public/js ' )) {
180120 return ;
181121 }
182122
183- $ this ->displayTask ('dumping & ignoring `public/js` folder ' , function () {
184- File::append (
185- base_path ('.gitignore ' ),
186- "\n/public/js \n"
187- );
188-
189- return self ::SUCCESS ;
190- });
123+ File::append (base_path ('.gitignore ' ), "\n/public/js \n" );
191124 }
192125
193- private function displayAfterNotes ()
126+ private function runStorageLinkCommand ()
194127 {
195- if (count ($ this ->afterMessages ) > 0 ) {
196- $ this ->displayHeader ('After Notes & Next Steps ' , '<bg=yellow;fg=black> NOTES </> ' );
197-
198- foreach ($ this ->afterMessages as $ message ) {
199- $ this ->line (' ' .$ message );
128+ if ($ this ->components ->confirm ('To be able to serve your assets in development, the resource/js folder will be symlinked to your public/js. Would you like to do that now? ' , true )) {
129+ if ($ this ->usingSail () && ! env ('LARAVEL_SAIL ' )) {
130+ Process::forever ()->run ([
131+ './vendor/bin/sail ' ,
132+ 'up ' ,
133+ '-d ' ,
134+ ], function ($ _type , $ output ) {
135+ $ this ->output ->write ($ output );
136+ });
137+
138+ Process::forever ()->run ([
139+ './vendor/bin/sail ' ,
140+ 'artisan ' ,
141+ 'storage:link ' ,
142+ ], function ($ _type , $ output ) {
143+ $ this ->output ->write ($ output );
144+ });
145+ } else {
146+ Process::forever ()->run ([
147+ $ this ->phpBinary (),
148+ 'artisan ' ,
149+ 'storage:link ' ,
150+ ], function ($ _type , $ output ) {
151+ $ this ->output ->write ($ output );
152+ });
200153 }
201154 }
202155 }
156+
157+ private function usingSail (): bool
158+ {
159+ return file_exists (base_path ('docker-compose.yml ' )) && str_contains (file_get_contents (base_path ('composer.json ' )), 'laravel/sail ' );
160+ }
161+
162+ private function phpBinary ()
163+ {
164+ return (new PhpExecutableFinder ())->find (false ) ?: 'php ' ;
165+ }
203166}
0 commit comments