roots / sage-installer

Sage 9 installer
https://github.com/roots/sage
MIT License
30 stars 85 forks source link

Laravel Mix #1

Open Log1x opened 6 years ago

Log1x commented 6 years ago

Really liking how sage-installer looks so far. Also +1 for adding Bulma.

Following up from sage Issue #1868.

My comment on the issue seen here gives exaggerated defaults that work with Sage that can most definitely be simplified.

Would like to see an option for Laravel Mix implemented. :)

Thanks in advance.

derkjn commented 6 years ago

I'm occurring in a lot of errors when using code splitting with this. Mix keeps creating a 0.js file in all the folders. Did this occur to you as well? It also breaks when running versioning and a few other issues when custom configs have to specified. I'm testing a few different options, I'll post my results when I get it working.

Log1x commented 6 years ago

Not at all. Not sure why that would be happening honestly.

derkjn commented 6 years ago

I can see there's a few active issues on their repo about this. It all boils down on how mix uses the publicPath. Weird enough though, even when I override this, the whole thing breaks down 😢

Edit: after a couple of hours of battling with this, it's definitely a publicPath issue in mix. When specified to work with a bedrock installation, it'll lose reference to the assets folders or break the path in the browser at runtime. It also doesn't append the correct path for on-demand bundles.

robbinjohansson commented 6 years ago

I've been playing around with Laravel Mix and Vuejs as a default setup with Sage and I would really like to have the option to install a fresh version of Sage with Laravel Mix and Vuejs as a preset.

It's been working out fine for me so far, built a few websites using this version of Sage.

Not sure how to go about creating a preset for the installer though 🙄

webstractions commented 6 years ago

I've worked with Mix on some Laravel apps and my first use in a stand-alone was with a new WordPress install. Got everything working fairly easy, except couldn't nail the public path part. But, that seemed to be a big issue for a number of folk over at the Mix repository.

Out of the blue, I found @Log1x Mix configs on Sage Issue #1868 -- which is now closed. Lots of good information in that, I would like to add onto that and address some concerns.

Getting the path right This can be a pain point, especially if someone on a Mac publishes his fix and guy like me (Windows) tries to use it. The following seems to work for me and is cross-OS. I am also including @Log1x variable setup, because the rest of my examples will reference them

let path = require('path');

const rootPath  = path.join(__dirname, '..');
const app       = 'app';
const resources = 'resources';
const assets    = 'resources/assets';
const dist      = 'dist';

mix.setPublicPath(dist);
mix.setResourceRoot(rootPath);

Copying assets @Log1x mentioned he had a problem with that. Not sure if he still is, I never did. FWIW and for posterity, here is how I do it.

// Assets
mix.copyDirectory(`${assets}/images`, `${dist}/images`);
   .copyDirectory(`${assets}/fonts`, `${dist}/fonts`);

Cross-OS package.json scripts Another pain point in the beginning was the scripts for Windows users. I do believe the newer versions of Mix have the following in package.json now. There is one more dependency for cross-env added too.

  "scripts": {
    "dev": "npm run development",
    "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch-poll": "npm run watch -- --watch-poll",
    "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
    "prod": "npm run production",
    "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
  },
  "devDependencies": {
    "cross-env": "^5.0.5",
  },

That is the stock Mix setup. For the normal Sage Yarn-ers, add the following aliases.

"scripts": {
    "build": npm run development
    "build:production": npm run production
    "start": npm run watch
  }

Working with versioning in Blade If you have versioning turned on for cache-busting purposes, there is a Blade directive for that. A typical Mix config that adds versioning for production distributions.

mix.js('resources/assets/js/app.js', 'dist/js');

if (mix.inProduction()) {
    mix.version();
}

In your Blade views, use the mix() directive.

<script src="{{ mix('/js/app.js') }}"></script>

Note that this is similar to the asset() directive. The mix() directive will call asset() and append the version hash to it via the mix.manifest.json file.

Caveat: Only problem here is that this will not work with wp_enqueue_script or wp_enqueue_style. To handle that, sage-lib component will have to account for that is some way. Any takers?

Over-riding the stock Webpack config Out of the box, Mix's webpack configuration does quite a bit. But sometimes you may want something added, removed, or tweaked. Let's say you want to add ESLint and compile with CoffeeScript:

// Add the dependencies
> yarn add eslint coffee-script coffee-loader

// Add ESLint rule and CoffeeScript via `mix.webpackConfig()`
if (process.env.NODE_ENV === 'development') {
  mix.webpackConfig({
    module: {
      rules: [
        {
          test: /\\.(js)$/,
          exclude: /node_modules/,
          loader: 'eslint-loader',
        },
      ],
    },
  });
}
mix.js('resources/assets/js/app.coffee', 'dist/js')
   .webpackConfig({
        module: {
            rules: [
                { test: /\.coffee$/, loader: 'coffee-loader' }
            ]
        }
   });

Edge case scenario I wanted to see if I could port a regular WordPress theme into Sage. I saw an article by Povilas Korop on Laravel Daily titled How to convert WordPress theme to Laravel Blade. It is pretty basic and didn't go into how to wade through a ton of css and js, so I dove in there.

I basically, grabbed all of the css/js stuff and HTML snapshots of a few pages. Dumped all of the non-dev tracking, ads, special plugin code (like the megamenu). This is all dumped into a single folder resources/parallax.

I move the main stylesheet and minified bootstrap (yup, easier this way) into the root of my resources\styles folder to rework section by section. This stylesheet gets enqueued after Main.scss.

Here are my Mix config and App\Setup for enqueing side-by-side, which BTW, kind of aids in development. It is really easy to compare with your enqueues.

/**
 * Theme assets
 */
add_action('wp_enqueue_scripts', function () {
    // Styles
    wp_enqueue_style('sage-bootstrap', asset_path('styles/bootstrap.min.css'), false, null);
    // wp_enqueue_style('sage-parallax',  asset_path('styles/parallax/parallax.css'), ['sage-bootstrap'], null, true);
    wp_enqueue_style('sage-main',      asset_path('styles/main.css'), ['sage-bootstrap'], null, null);
    wp_enqueue_style('sage-style',      asset_path('styles/style.css'), ['sage-main'], null, null);
    // Scripts
    wp_enqueue_script('sage-bootstrap', asset_path('scripts/bootstrap.min.js'), ['jquery'], null);
    wp_enqueue_script('sage-parallax',  asset_path('scripts/parallax.js'), ['jquery'], null);
    wp_enqueue_script('sage-js',        asset_path('scripts/main.js'), ['jquery', 'sage-bootstrap'], null);
}, 100);
// Assets
mix.copyDirectory(`${assets}/images`, `${dist}/images`);
mix.copyDirectory(`${assets}/fonts`, `${dist}/fonts`);

// Styles
mix.copyDirectory(`${resources}/parallax/*.css`, `${dist}/styles/parallax`)
   .copy(`${assets}/styles/bootstrap.min.css`, `${dist}/styles`)
   .sass(`${assets}/styles/main.scss`, `${dist}/styles`)
   .copy(`${assets}/styles/style.css`, `${dist}/styles`);

// Scripts
mix.js(`${assets}/scripts/main.js`, `${dist}/scripts`)
   .js(`${assets}/scripts/customizer.js`, `${dist}/scripts`)
   .copy(`${assets}/scripts/bootstrap.min.js`, `${dist}/scripts`)
   .combine(`${assets}/scripts/parallax/*.js`, `${dist}/scripts/parallax.js`);

// PostCSS
mix.options({
  processCssUrls: false,
});

Note the mix.combine() method. This will get minified when you yarn build:production or npm run production. There is mix.minify() method which does the same thing as combine, but it adds the .min to the filename.

Well this turned into a longer winded post than it should have been. Hopefully somebody will get some use out of it.

greatislander commented 6 years ago

@webstractions re:

Caveat: Only problem here is that this will not work with wp_enqueue_script or wp_enqueue_style. To handle that, sage-lib component will have to account for that is some way. Any takers?

I fixed this, perhaps in not the most elegant way, by changing this line in config/assets.php (assuming that's where you put your mix-manifest.json):

--    'manifest' => get_theme_file_path().'/dist/assets.json',
++    'manifest' => get_theme_file_path().'/dist/mix-manifest.json',

And by modifying Sage's asset_path function as follows:

/**
 * @param $asset
 * @return string
 */
function asset_path($asset)
{
--    return sage('assets')->getUri('/' . $asset);
++    return str_replace('/dist//', '/dist/', sage('assets')->getUri('/' . $asset));
}

This normalizes for Laravel Mix's practice of adding a leading slash to the asset paths in mix-manifest.json.

These two changes let me use Mix without having to patch sage-lib.

webstractions commented 6 years ago

@greatislander Cool. I had already revised the manifest. Didn't know about the asset_path quirk, but I haven't had any problems with that using the path setups that I outlined above. Maybe I can dump most of that and do your fix.

With that said, how would you go about doing wp_enqueues without knowing the version hashes in advance? You have to explicitly set those in your wp_enqueues beforehand, which would be a pain to do.

Log1x commented 6 years ago

My Mix file looks somewhat like this now...

const mix            = require('laravel-mix');
const ImageminPlugin = require('imagemin-webpack-plugin').default;

/*
 |--------------------------------------------------------------------------
 | 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.
 |
 */

const app       = 'app';
const config    = 'config';
const resources = 'resources';
const assets    = `${resources}/assets`;
const dist      = 'dist';

mix.setPublicPath(dist);
mix.setResourceRoot('../');

// Browser Sync
mix.browserSync({
  host: 'localhost',
  proxy: 'https://flux.dev',
  port: 3000,
  browser: ['firefox.exe'],

  files: [
    `${app}/**/*.php`,
    `${config}/**/*.php`,
    `${resources}/views/**/*.php`,
    `${dist}/styles/**.css`,
    `${dist}/scripts/**.js`
  ]
});

// Stylus
mix.stylus(`${assets}/styles/wrapper.styl`, `${dist}/styles/main.css`)
   .stylus(`${assets}/styles/wordpress/editor.styl`, `${dist}/styles/editor.css`)
   .stylus(`${assets}/styles/wordpress/admin/wrapper.styl`, `${dist}/styles/admin.css`)
   .stylus(`${assets}/styles/wordpress/login/wrapper.styl`, `${dist}/styles/login.css`);

// Javascript
mix.js(`${assets}/scripts/main.js`, `${dist}/scripts`)
   .js(`${assets}/scripts/tinymce.js`, `${dist}/scripts`)
   .extract([
      'jquery-sticky/jquery.sticky',
      'readmore-js/readmore.min'
    ]);

// Assets
mix.copyDirectory(`${assets}/fonts`, `${dist}/fonts`)
   .copyDirectory(`${assets}/images`, `${dist}/images`)
   .copyDirectory(`${assets}/favicons`, `${dist}/favicons`);

// Autoload
mix.autoload({
  jquery: ['$', 'window.jQuery', 'jQuery']
});

// Options
mix.options({
  processCssUrls: false,
  postCss: [
    require('rucksack-css')()
  ]
});

// Additional Configuration
mix.webpackConfig({
  plugins: [
    new ImageminPlugin({
      pngquant: {
        quality: '95-100'
      },
      test: /\.(jpe?g|png|gif|svg)$/i
    })
  ]
});

// Source maps when not in production.
if (!mix.inProduction()) {
  mix.sourceMaps();
}

// Hash and version files in production.
if (mix.inProduction()) {
  mix.version();
}

.extract() is optional but I like separating my vendor stuff into vendor.js.

My scripts:

  "scripts": {
    "build": "NODE_ENV=development webpack --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "build:production": "npm run -s clean && NODE_ENV=production webpack --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js && npm run -s build:views",
    "build:views": "wp blade compile",
    "build:fonts": "icon-font-generator resources/assets/fonts/icons/src/svg/**/*.svg --out 'resources/assets/fonts/icons/' --csstp 'resources/assets/fonts/icons/src/config.hbs' --csspath 'resources/assets/styles/config/icons.styl' --html 'false' --json 'false'",
    "start": "NODE_ENV=development webpack --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "clean": "npm run -s clean:cache && npm run -s clean:dist",
    "clean:cache": "rimraf resources/cache",
    "clean:dist": "rimraf dist",
    "lint": "npm run -s lint:scripts && npm run -s lint:styles",
    "lint:scripts": "eslint resources/assets/scripts",
    "lint:styles": "stylelint 'resources/assets/styles/**/*.{css,styl,sass,scss,less}'",
    "test": "npm run -s lint"
  },
greatislander commented 6 years ago

@webstractions For wp_enqueue, this "just works" for me, as the version hash is included in the URL:

wp_enqueue_style('sage/main.css', asset_path('styles/main.css'), false, null);

The asset_path quirk isn't a big deal, it just cleans up a double slash:

-- /app/themes/sage/dist//styles/main.css
++ /app/themes/sage/dist/styles/main.css
webstractions commented 6 years ago

@greatislander Okay. It is all clear now. Forgot about the asset_path() helper function.

On a side note, I was checking out asset() and mix() in a blade file. Guess what? It fails,

// Throw this into a blade template
<script src="{{ asset('/js/main.js') }}"></script>

// And you get a white screen of death
Fatal error: Uncaught Symfony\Component\Debug\Exception\FatalThrowableError: Call to undefined function asset() in C:\xampp\htdocs\sites\w .... yada ... yada

After investigating a bit, all of those helpers get routed through Illuminate\Routing\UrlGenerator which is not installed in Sage.

Since assets_path just works, I will use that instead. In a blade file: <script src="{{ App\asset_path('/js/main.js') }}"></script>

Been playing with BrowserSync. Love that tunnel option!

// Browser Sync
mix.browserSync({
  host: 'localhost',
  proxy: 'larallax.loc',
  port: 3000,
  tunnel: true,

  files: [
    `${app}/**/*.php`,
    `${resources}/**/*.php`,
    `${dist}/**/*.css`,
    `${dist}/**/*.js`,
  ],
});

Then yarn watch and you will get your tunnel.

[Browsersync] Proxying: http://larallax.loc
[Browsersync] Access URLs:
 ----------------------------------------------
       Local: http://localhost:3000
    External: http://10.0.0.120:3000
      Tunnel: https://ilyqpubsuj.localtunnel.me     <---- Try this at a coffeeshop with your client
 ----------------------------------------------
          UI: http://localhost:3001
 UI External: http://10.0.0.120:3001
 ----------------------------------------------
[Browsersync] Watching files...
greatislander commented 6 years ago

@webstractions Yeah, I didn't try using mix() or asset(), just hacked at Sage's asset_path().

retlehs commented 6 years ago

we'll be switching to laravel mix as the default instead of making it an option during install https://github.com/roots/sage/pull/2011