Laravel-mix: When attempting to compile any more than 9 .less files, the number of resulting .css files are always one short

Created on 19 Jan 2018  路  4Comments  路  Source: JeffreyWay/laravel-mix

  • Laravel Mix Version: 1.7.2
  • Node Version: 9.4.0
  • NPM Version: 5.6.0
  • OS: MacOS High Sierra

Description:

When attempting to compile any more than 9 less files (via mix.less), the number of resulting .css files are always one short.

I ran the following tests:

  • If I try to compile 12 .less files, only 11 css files are produced
  • If I try to compile 11 .less files, only 10 css files are produced
  • If I try to compile 10 .less files, only 9 css files are produced
  • BUT If I try to compile 9 .less files, then the correct amount of css files are produced, 9.
  • Likewise, if I compile 8 .less files, then the correct amount of css files are produced, 8.

Steps To Reproduce:

I replicated my problem in a fresh Laravel install which is available here:
https://github.com/susanBuck/foobar.

webpack.mix.js is as follows:

let mix = require('laravel-mix');

mix.less('resources/less/a.less', 'css/')
    .less('resources/less/b.less', 'css/')
    .less('resources/less/c.less', 'css/')
    .less('resources/less/d.less', 'css/')

    .less('resources/less/e.less', 'css/')
    .less('resources/less/f.less', 'css/')
    .less('resources/less/g.less', 'css/')
    .less('resources/less/h.less', 'css/')

    .less('resources/less/i.less', 'css/')
    .less('resources/less/j.less', 'css/')
    .less('resources/less/k.less', 'css/')
    .less('resources/less/l.less', 'css/');

And below are the details of 5 different tests that vary the amount of mix.less calls by commenting them out.

Between each test, I run this command...

```bash
rm -rf ./public/css; npm run dev; find ./public/css -type f -and -not -name ".DS_STORE" | wc -l
````

...in order to:

  1. Wipe public/css (destination folder) clean for a blank slate
  2. Run npm run dev to invoke Mix
  3. Report the number of resulting .css files that were produced in public/css

Test 1:
Leave all 12 .less statements as is.
Invoke above-mentioned command.
.css file count:

  • Expected: 12
  • Actual: 11
    _FAIL_

Test 2
Comment any ONE of the .less statements so only 11 are run.
Invoke above-mentioned command.
.css file count:

  • Expected: 11
  • Actual: 10
    _FAIL_

Test 3
Comment any TWO of the .less statements so only 10 are run.
Invoke above-mentioned command.
.css file count:

  • Expected: 10
  • Actual: 9
    _FAIL_

Test 4
Comment out any THREE of the .less statements so only 9 are run.
Invoke above-mentioned command.
.css file count:

  • Expected: 9
  • Actual: 9
    _PASS_

Test 5
Comment out any FOUR of the .less statements so only 8 are run.
Invoke above-mentioned command.

  • Expected: 8
  • Actual: 8
    _PASS_

Misc details

Here's the console output when attempting to compile 12 less files (as demonstrated in Test 1). It mirrors my file count from the test (11... one short).

image

Most helpful comment

Thanks! That's fixed now, and will be part of the next release.

All 4 comments

Additional note: I've just noticed that when I compile <= 9 .less files (i.e. passing cases), mix.js is not included in the output of compiled Assets.

Digging into the source, I learned that the mix.js file is a temporary file created when no js compilation is requested.

From laravel-mix/src/plugins/MockEntryPlugin.js:

class MockEntryPlugin {
    /**
     * Handle the deletion of the temporary mix.js
     * output file that was generated by webpack.
     *
     * This file is created when the user hasn't
     * requested any JavaScript compilation, but
     * webpack still requires an entry.
     *
     * @param {Object} compiler
     */

Knowing this, as an experiment I added a single mix.js() invocation in my webpack.mix.js file and __it resolves the problem__.

let mix = require('laravel-mix');

mix.less('resources/less/a.less', 'css/')
    .less('resources/less/b.less', 'css/')
    .less('resources/less/c.less', 'css/')
    .less('resources/less/d.less', 'css/')

    .less('resources/less/e.less', 'css/')
    .less('resources/less/f.less', 'css/')
    .less('resources/less/g.less', 'css/')
    .less('resources/less/h.less', 'css/')

    .less('resources/less/i.less', 'css/')
    .less('resources/less/j.less', 'css/')
    .less('resources/less/k.less', 'css/')
    .less('resources/less/l.less', 'css/');

// As long as there's just one mix.js call, all 12 .less files are compiled
mix.js('resources/assets/js/bootstrap.js', 'js/'); 

So, I've got a work-around for my app, but seems like there's still some underlying problem.

Found the source of the problem in laravel-mix/src/plugins/MockEntryPlugin.js:

class MockEntryPlugin {
    /**
     * Handle the deletion of the temporary mix.js
     * output file that was generated by webpack.
     *
     * This file is created when the user hasn't
     * requested any JavaScript compilation, but
     * webpack still requires an entry.
     *
     * @param {Object} compiler
     */
    apply(compiler) {
        compiler.plugin('done', stats => {
            let temporaryOutputFile = stats.toJson()
                .assets
                .find(asset => asset.chunkNames.includes('mix'));

            if (temporaryOutputFile) {
                delete stats.compilation.assets[temporaryOutputFile.name];
                File.find(
                    path.resolve(Config.publicPath, temporaryOutputFile.name)
                ).delete();
            }
        });
    }
}

It locates the temporary mix file with this line:

let temporaryOutputFile = stats.toJson()
                .assets
                .find(asset => asset.chunkNames.includes('mix'));

When I console.log stats.toJson().assets I see the following output. Notice that the very first item css/f.css has a chunkName of mix (they all do, actually). Thus, the above line targets this as the temporary mix.js file and deletes it. But it's not the temporary mix.js file, it's my app's CSS file!

[ { name: 'css/f.css',
    size: 13,
    chunks: [ 0 ],
    chunkNames: [ 'mix' ],
    emitted: true,
    isOverSizeLimit: undefined },
  { name: 'mix.js',
    size: 4051,
    chunks: [ 0 ],
    chunkNames: [ 'mix' ],
    emitted: true,
    isOverSizeLimit: undefined },
  { name: 'css/b.css',
    size: 13,
    chunks: [ 0 ],
    chunkNames: [ 'mix' ],
    emitted: true,
    isOverSizeLimit: undefined },
  { name: 'css/c.css',
    size: 13,
    chunks: [ 0 ],
    chunkNames: [ 'mix' ],
    emitted: true,
    isOverSizeLimit: undefined },
  { name: 'css/d.css',
    size: 13,
    chunks: [ 0 ],
    chunkNames: [ 'mix' ],
    emitted: true,
    isOverSizeLimit: undefined },
  { name: 'css/e.css',
    size: 13,
    chunks: [ 0 ],
    chunkNames: [ 'mix' ],
    emitted: true,
    isOverSizeLimit: undefined },
  { name: 'css/a.css',
    size: 13,
    chunks: [ 0 ],
    chunkNames: [ 'mix' ],
    emitted: true,
    isOverSizeLimit: undefined },
  { name: 'css/g.css',
    size: 13,
    chunks: [ 0 ],
    chunkNames: [ 'mix' ],
    emitted: true,
    isOverSizeLimit: undefined },
  { name: 'css/h.css',
    size: 13,
    chunks: [ 0 ],
    chunkNames: [ 'mix' ],
    emitted: true,
    isOverSizeLimit: undefined },
  { name: 'css/i.css',
    size: 13,
    chunks: [ 0 ],
    chunkNames: [ 'mix' ],
    emitted: true,
    isOverSizeLimit: undefined },
  { name: 'css/j.css',
    size: 13,
    chunks: [ 0 ],
    chunkNames: [ 'mix' ],
    emitted: true,
    isOverSizeLimit: undefined },
  { name: 'css/k.css',
    size: 13,
    chunks: [ 0 ],
    chunkNames: [ 'mix' ],
    emitted: true,
    isOverSizeLimit: undefined },
  { name: 'css/l.css',
    size: 13,
    chunks: [ 0 ],
    chunkNames: [ 'mix' ],
    emitted: true,
    isOverSizeLimit: undefined } ]

If I amend the definition of temporaryOutputFile to search on name instead of chunkNames the problem is solved. Also amended it to more specifically look for mix.js instead of mix.

Before:

 let temporaryOutputFile = stats.toJson()
    .assets
    .find(asset => asset.chunkNames.includes('mix'));

After:

let temporaryOutputFile = stats.toJson()
    .assets
    .find(asset => asset.chunk.includes('mix.js'));

Thanks! That's fixed now, and will be part of the next release.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

RomainGoncalves picture RomainGoncalves  路  3Comments

kpilard picture kpilard  路  3Comments

terion-name picture terion-name  路  3Comments

wendt88 picture wendt88  路  3Comments

rlewkowicz picture rlewkowicz  路  3Comments