diff --git a/README.md b/README.md index b40d3df..f9e3892 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,8 @@ Only the latest major version of Laravel UI receives bug fixes. The table below |---- |----| | [1.x](https://github.com/laravel/ui/tree/1.x) | 5.8, 6.x | | [2.x](https://github.com/laravel/ui/tree/2.x) | 7.x | -| [3.x](https://github.com/laravel/ui/tree/3.x) | 8.x, 9.x | +| [3.x](https://github.com/laravel/ui/tree/3.x) | 8.x | +| [4.x](https://github.com/laravel/ui/tree/4.x) | 9.x | ### Installation @@ -46,11 +47,11 @@ php artisan ui react --auth #### CSS -[Laravel Mix](https://laravel.com/docs/mix) provides a clean, expressive API over compiling SASS or Less, which are extensions of plain CSS that add variables, mixins, and other powerful features that make working with CSS much more enjoyable. In this document, we will briefly discuss CSS compilation in general; however, you should consult the full [Laravel Mix documentation](https://laravel.com/docs/mix) for more information on compiling SASS or Less. +Laravel officially supports [Vite](https://laravel.com/docs/vite), a modern frontend build tool that provides an extremely fast development environment and bundles your code for production. Vite supports a variety of CSS preprocessor languages, including SASS and Less, which are extensions of plain CSS that add variables, mixins, and other powerful features that make working with CSS much more enjoyable. In this document, we will briefly discuss CSS compilation in general; however, you should consult the full [Vite documentation](https://laravel.com/docs/vite#working-with-stylesheets) for more information on compiling SASS or Less. #### JavaScript -Laravel does not require you to use a specific JavaScript framework or library to build your applications. In fact, you don't have to use JavaScript at all. However, Laravel does include some basic scaffolding to make it easier to get started writing modern JavaScript using the [Vue](https://vuejs.org) library. Vue provides an expressive API for building robust JavaScript applications using components. As with CSS, we may use Laravel Mix to easily compile JavaScript components into a single, browser-ready JavaScript file. +Laravel does not require you to use a specific JavaScript framework or library to build your applications. In fact, you don't have to use JavaScript at all. However, Laravel does include some basic scaffolding to make it easier to get started writing modern JavaScript using the [Vue](https://vuejs.org) library. Vue provides an expressive API for building robust JavaScript applications using components. As with CSS, we may use Vite to easily compile JavaScript components into a single, browser-ready JavaScript file. ### Writing CSS @@ -62,13 +63,13 @@ Before compiling your CSS, install your project's frontend dependencies using th npm install ``` -Once the dependencies have been installed using `npm install`, you can compile your SASS files to plain CSS using [Laravel Mix](https://laravel.com/docs/mix#working-with-stylesheets). The `npm run dev` command will process the instructions in your `webpack.mix.js` file. Typically, your compiled CSS will be placed in the `public/css` directory: +Once the dependencies have been installed using `npm install`, you can compile your SASS files to plain CSS using [Vite](https://laravel.com/docs/vite#working-with-stylesheets). The `npm run dev` command will process the instructions in your `vite.config.js` file. Typically, your compiled CSS will be placed in the `public/build/assets` directory: ```bash npm run dev ``` -The `webpack.mix.js` file included with Laravel's frontend scaffolding will compile the `resources/sass/app.scss` SASS file. This `app.scss` file imports a file of SASS variables and loads Bootstrap, which provides a good starting point for most applications. Feel free to customize the `app.scss` file however you wish or even use an entirely different pre-processor by [configuring Laravel Mix](https://laravel.com/docs/mix). +The `vite.config.js` file included with Laravel's frontend scaffolding will compile the `resources/sass/app.scss` SASS file. This `app.scss` file imports a file of SASS variables and loads Bootstrap, which provides a good starting point for most applications. Feel free to customize the `app.scss` file however you wish or even use an entirely different pre-processor by [configuring Vite](https://laravel.com/docs/vite#working-with-stylesheets). ### Writing JavaScript @@ -80,13 +81,13 @@ npm install > By default, the Laravel `package.json` file includes a few packages such as `lodash` and `axios` to help you get started building your JavaScript application. Feel free to add or remove from the `package.json` file as needed for your own application. -Once the packages are installed, you can use the `npm run dev` command to [compile your assets](https://laravel.com/docs/mix). Webpack is a module bundler for modern JavaScript applications. When you run the `npm run dev` command, Webpack will execute the instructions in your `webpack.mix.js` file: +Once the packages are installed, you can use the `npm run dev` command to [compile your assets](https://laravel.com/docs/vite). Vite is a module bundler for modern JavaScript applications. When you run the `npm run dev` command, Vite will execute the instructions in your `vite.config.js` file: ```bash npm run dev ``` -By default, the Laravel `webpack.mix.js` file compiles your SASS and the `resources/js/app.js` file. Within the `app.js` file you may register your Vue components or, if you prefer a different framework, configure your own JavaScript application. Your compiled JavaScript will typically be placed in the `public/js` directory. +By default, the Laravel `vite.config.js` file compiles your SASS and the `resources/js/app.js` file. Within the `app.js` file you may register your Vue components or, if you prefer a different framework, configure your own JavaScript application. Your compiled JavaScript will typically be placed in the `public/build/assets` directory. > The `app.js` file will load the `resources/js/bootstrap.js` file which bootstraps and configures Vue, Axios, jQuery, and all other JavaScript dependencies. If you have additional JavaScript dependencies to configure, you may do so in this file. @@ -95,10 +96,8 @@ By default, the Laravel `webpack.mix.js` file compiles your SASS and the `resour When using the `laravel/ui` package to scaffold your frontend, an `ExampleComponent.vue` Vue component will be placed in the `resources/js/components` directory. The `ExampleComponent.vue` file is an example of a [single file Vue component](https://vuejs.org/guide/single-file-components) which defines its JavaScript and HTML template in the same file. Single file components provide a very convenient approach to building JavaScript driven applications. The example component is registered in your `app.js` file: ```javascript -Vue.component( - 'example-component', - require('./components/ExampleComponent.vue').default -); +import ExampleComponent from './components/ExampleComponent.vue'; +Vue.component('example-component', ExampleComponent); ``` To use the component in your application, you may drop it into one of your HTML templates. For example, after running the `php artisan ui vue --auth` Artisan command to scaffold your application's authentication and registration screens, you could drop the component into the `home.blade.php` Blade template: diff --git a/src/Auth/bootstrap-stubs/layouts/app.stub b/src/Auth/bootstrap-stubs/layouts/app.stub index 9961df7..f8af491 100644 --- a/src/Auth/bootstrap-stubs/layouts/app.stub +++ b/src/Auth/bootstrap-stubs/layouts/app.stub @@ -9,15 +9,12 @@ {{ config('app.name', 'Laravel') }} - - - - - + + @vite(['resources/sass/app.scss', 'resources/js/app.js'])
diff --git a/src/Presets/Bootstrap.php b/src/Presets/Bootstrap.php index bef6fbe..f94a9b8 100644 --- a/src/Presets/Bootstrap.php +++ b/src/Presets/Bootstrap.php @@ -14,7 +14,7 @@ class Bootstrap extends Preset public static function install() { static::updatePackages(); - static::updateWebpackConfiguration(); + static::updateViteConfiguration(); static::updateSass(); static::updateBootstrapping(); static::removeNodeModules(); @@ -37,13 +37,13 @@ protected static function updatePackageArray(array $packages) } /** - * Update the Webpack configuration. + * Update the Vite configuration. * * @return void */ - protected static function updateWebpackConfiguration() + protected static function updateViteConfiguration() { - copy(__DIR__.'/bootstrap-stubs/webpack.mix.js', base_path('webpack.mix.js')); + copy(__DIR__.'/bootstrap-stubs/vite.config.js', base_path('vite.config.js')); } /** diff --git a/src/Presets/React.php b/src/Presets/React.php index 59d2e15..126c487 100644 --- a/src/Presets/React.php +++ b/src/Presets/React.php @@ -16,9 +16,10 @@ public static function install() { static::ensureComponentDirectoryExists(); static::updatePackages(); - static::updateWebpackConfiguration(); + static::updateViteConfiguration(); static::updateBootstrapping(); static::updateComponent(); + static::addViteReactRefreshDirective(); static::removeNodeModules(); } @@ -32,19 +33,20 @@ protected static function updatePackageArray(array $packages) { return [ '@babel/preset-react' => '^7.13.13', + '@vitejs/plugin-react' => '^1.3.2', 'react' => '^17.0.2', 'react-dom' => '^17.0.2', - ] + Arr::except($packages, ['vue', 'vue-template-compiler']); + ] + Arr::except($packages, ['@vitejs/plugin-vue', 'vue']); } /** - * Update the Webpack configuration. + * Update the Vite configuration. * * @return void */ - protected static function updateWebpackConfiguration() + protected static function updateViteConfiguration() { - copy(__DIR__.'/react-stubs/webpack.mix.js', base_path('webpack.mix.js')); + copy(__DIR__.'/react-stubs/vite.config.js', base_path('vite.config.js')); } /** @@ -59,8 +61,8 @@ protected static function updateComponent() ); copy( - __DIR__.'/react-stubs/Example.js', - resource_path('js/components/Example.js') + __DIR__.'/react-stubs/Example.jsx', + resource_path('js/components/Example.jsx') ); } @@ -73,4 +75,36 @@ protected static function updateBootstrapping() { copy(__DIR__.'/react-stubs/app.js', resource_path('js/app.js')); } + + /** + * Add Vite's React Refresh Runtime + * + * @return void + */ + protected static function addViteReactRefreshDirective() + { + $view = static::getViewPath('layouts/app.blade.php'); + + if (! file_exists($view)) { + return; + } + + file_put_contents( + $view, + str_replace('@vite(', '@viteReactRefresh'.PHP_EOL.' @vite(', file_get_contents($view)) + ); + } + + /** + * Get full view path relative to the application's configured view path. + * + * @param string $path + * @return string + */ + protected static function getViewPath($path) + { + return implode(DIRECTORY_SEPARATOR, [ + config('view.paths')[0] ?? resource_path('views'), $path, + ]); + } } diff --git a/src/Presets/Vue.php b/src/Presets/Vue.php index 6cb9212..45a2953 100644 --- a/src/Presets/Vue.php +++ b/src/Presets/Vue.php @@ -16,7 +16,7 @@ public static function install() { static::ensureComponentDirectoryExists(); static::updatePackages(); - static::updateWebpackConfiguration(); + static::updateViteConfiguration(); static::updateBootstrapping(); static::updateComponent(); static::removeNodeModules(); @@ -31,26 +31,27 @@ public static function install() protected static function updatePackageArray(array $packages) { return [ + '@vitejs/plugin-vue' => '^2.3.3', 'resolve-url-loader' => '^3.1.2', 'sass' => '^1.32.11', 'sass-loader' => '^11.0.1', - 'vue' => '^2.6.12', - 'vue-template-compiler' => '^2.6.12', + 'vue' => '^3.2.37', ] + Arr::except($packages, [ '@babel/preset-react', + '@vitejs/plugin-react', 'react', 'react-dom', ]); } /** - * Update the Webpack configuration. + * Update the Vite configuration. * * @return void */ - protected static function updateWebpackConfiguration() + protected static function updateViteConfiguration() { - copy(__DIR__.'/vue-stubs/webpack.mix.js', base_path('webpack.mix.js')); + copy(__DIR__.'/vue-stubs/vite.config.js', base_path('vite.config.js')); } /** diff --git a/src/Presets/bootstrap-stubs/app.scss b/src/Presets/bootstrap-stubs/app.scss index 3193ffa..81aa760 100644 --- a/src/Presets/bootstrap-stubs/app.scss +++ b/src/Presets/bootstrap-stubs/app.scss @@ -5,4 +5,4 @@ @import 'variables'; // Bootstrap -@import '~bootstrap/scss/bootstrap'; +@import 'bootstrap/scss/bootstrap'; diff --git a/src/Presets/bootstrap-stubs/bootstrap.js b/src/Presets/bootstrap-stubs/bootstrap.js index dcdc4df..3c4dd4d 100644 --- a/src/Presets/bootstrap-stubs/bootstrap.js +++ b/src/Presets/bootstrap-stubs/bootstrap.js @@ -1,8 +1,7 @@ -window._ = require('lodash'); +import _ from 'lodash'; +window._ = _; -try { - require('bootstrap'); -} catch (e) {} +import 'bootstrap'; /** * We'll load the axios HTTP library which allows us to easily issue requests @@ -10,7 +9,8 @@ try { * CSRF token as a header based on the value of the "XSRF" token cookie. */ -window.axios = require('axios'); +import axios from 'axios'; +window.axios = axios; window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; @@ -22,11 +22,15 @@ window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; // import Echo from 'laravel-echo'; -// window.Pusher = require('pusher-js'); +// import Pusher from 'pusher-js'; +// window.Pusher = Pusher; // window.Echo = new Echo({ // broadcaster: 'pusher', -// key: process.env.MIX_PUSHER_APP_KEY, -// cluster: process.env.MIX_PUSHER_APP_CLUSTER, -// forceTLS: true +// key: import.meta.env.VITE_PUSHER_APP_KEY, +// wsHost: import.meta.env.VITE_PUSHER_HOST ?? `ws-${import.meta.env.VITE_PUSHER_CLUSTER}.pusher.com`, +// wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80, +// wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443, +// forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https', +// enabledTransports: ['ws', 'wss'], // }); diff --git a/src/Presets/bootstrap-stubs/vite.config.js b/src/Presets/bootstrap-stubs/vite.config.js new file mode 100644 index 0000000..9264c75 --- /dev/null +++ b/src/Presets/bootstrap-stubs/vite.config.js @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel([ + 'resources/sass/app.scss', + 'resources/js/app.js', + ]), + ], +}); diff --git a/src/Presets/bootstrap-stubs/webpack.mix.js b/src/Presets/bootstrap-stubs/webpack.mix.js deleted file mode 100644 index 1a2a958..0000000 --- a/src/Presets/bootstrap-stubs/webpack.mix.js +++ /dev/null @@ -1,16 +0,0 @@ -const mix = require('laravel-mix'); - -/* - |-------------------------------------------------------------------------- - | Mix Asset Management - |-------------------------------------------------------------------------- - | - | Mix provides a clean, fluent API for defining some Webpack build steps - | for your Laravel application. By default, we are compiling the Sass - | file for the application as well as bundling up all the JS files. - | - */ - -mix.js('resources/js/app.js', 'public/js') - .sass('resources/sass/app.scss', 'public/css') - .sourceMaps(); diff --git a/src/Presets/react-stubs/Example.js b/src/Presets/react-stubs/Example.jsx similarity index 100% rename from src/Presets/react-stubs/Example.js rename to src/Presets/react-stubs/Example.jsx diff --git a/src/Presets/react-stubs/app.js b/src/Presets/react-stubs/app.js index a5f91ab..46d7554 100644 --- a/src/Presets/react-stubs/app.js +++ b/src/Presets/react-stubs/app.js @@ -4,7 +4,7 @@ * building robust, powerful web applications using React + Laravel. */ -require('./bootstrap'); +import './bootstrap'; /** * Next, we will create a fresh React component instance and attach it to @@ -12,4 +12,4 @@ require('./bootstrap'); * or customize the JavaScript scaffolding to fit your unique needs. */ -require('./components/Example'); +import './components/Example'; diff --git a/src/Presets/react-stubs/vite.config.js b/src/Presets/react-stubs/vite.config.js new file mode 100644 index 0000000..fabba91 --- /dev/null +++ b/src/Presets/react-stubs/vite.config.js @@ -0,0 +1,13 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [ + laravel([ + 'resources/sass/app.scss', + 'resources/js/app.js', + ]), + react(), + ], +}); diff --git a/src/Presets/react-stubs/webpack.mix.js b/src/Presets/react-stubs/webpack.mix.js deleted file mode 100644 index 6d8f835..0000000 --- a/src/Presets/react-stubs/webpack.mix.js +++ /dev/null @@ -1,16 +0,0 @@ -const mix = require('laravel-mix'); - -/* - |-------------------------------------------------------------------------- - | Mix Asset Management - |-------------------------------------------------------------------------- - | - | Mix provides a clean, fluent API for defining some Webpack build steps - | for your Laravel application. By default, we are compiling the Sass - | file for the application as well as bundling up all the JS files. - | - */ - -mix.js('resources/js/app.js', 'public/js') - .react() - .sass('resources/sass/app.scss', 'public/css'); diff --git a/src/Presets/vue-stubs/app.js b/src/Presets/vue-stubs/app.js index 9ae9348..d007f50 100644 --- a/src/Presets/vue-stubs/app.js +++ b/src/Presets/vue-stubs/app.js @@ -4,9 +4,19 @@ * building robust, powerful web applications using Vue and Laravel. */ -require('./bootstrap'); +import './bootstrap'; +import { createApp } from 'vue'; -window.Vue = require('vue').default; +/** + * Next, we will create a fresh Vue application instance. You may then begin + * registering components with the application instance so they are ready + * to use in your application's views. An example is included for you. + */ + +const app = createApp({}); + +import ExampleComponent from './components/ExampleComponent.vue'; +app.component('example-component', ExampleComponent); /** * The following block of code may be used to automatically register your @@ -16,17 +26,14 @@ window.Vue = require('vue').default; * Eg. ./components/ExampleComponent.vue -> */ -// const files = require.context('./', true, /\.vue$/i) -// files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(key).default)) - -Vue.component('example-component', require('./components/ExampleComponent.vue').default); +// Object.entries(import.meta.globEager('./**/*.vue')).forEach(([path, definition]) => { +// app.component(path.split('/').pop().replace(/\.\w+$/, ''), definition.default); +// }); /** - * Next, we will create a fresh Vue application instance and attach it to - * the page. Then, you may begin adding components to this application - * or customize the JavaScript scaffolding to fit your unique needs. + * Finally, we will attach the application instance to a HTML element with + * an "id" attribute of "app". This element is included with the "auth" + * scaffolding. Otherwise, you will need to add an element yourself. */ -const app = new Vue({ - el: '#app', -}); +app.mount('#app'); diff --git a/src/Presets/vue-stubs/vite.config.js b/src/Presets/vue-stubs/vite.config.js new file mode 100644 index 0000000..9709d3d --- /dev/null +++ b/src/Presets/vue-stubs/vite.config.js @@ -0,0 +1,25 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; +import vue from '@vitejs/plugin-vue'; + +export default defineConfig({ + plugins: [ + laravel([ + 'resources/sass/app.scss', + 'resources/js/app.js', + ]), + vue({ + template: { + transformAssetUrls: { + base: null, + includeAbsolute: false, + }, + }, + }), + ], + resolve: { + alias: { + vue: 'vue/dist/vue.esm-bundler.js', + }, + }, +}); diff --git a/src/Presets/vue-stubs/webpack.mix.js b/src/Presets/vue-stubs/webpack.mix.js deleted file mode 100644 index 6189710..0000000 --- a/src/Presets/vue-stubs/webpack.mix.js +++ /dev/null @@ -1,16 +0,0 @@ -const mix = require('laravel-mix'); - -/* - |-------------------------------------------------------------------------- - | Mix Asset Management - |-------------------------------------------------------------------------- - | - | Mix provides a clean, fluent API for defining some Webpack build steps - | for your Laravel application. By default, we are compiling the Sass - | file for the application as well as bundling up all the JS files. - | - */ - -mix.js('resources/js/app.js', 'public/js') - .vue() - .sass('resources/sass/app.scss', 'public/css'); diff --git a/src/UiCommand.php b/src/UiCommand.php index 58a707f..0315d1e 100644 --- a/src/UiCommand.php +++ b/src/UiCommand.php @@ -41,11 +41,11 @@ public function handle() throw new InvalidArgumentException('Invalid preset.'); } - $this->{$this->argument('type')}(); - if ($this->option('auth')) { $this->call('ui:auth'); } + + $this->{$this->argument('type')}(); } /**