Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When attempting to compile any more than 9 .less files, the number of resulting .css files are always one short #1420

Closed
susanBuck opened this issue Jan 19, 2018 · 4 comments · Fixed by m1guelpf/miguelpiedrafita.com#13

Comments

@susanBuck
Copy link

susanBuck commented Jan 19, 2018

  • 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...

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

@susanBuck
Copy link
Author

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.

@susanBuck
Copy link
Author

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.

@susanBuck
Copy link
Author

susanBuck commented Jan 19, 2018

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'));

susanBuck added a commit to susanBuck/laravel-mix that referenced this issue Jan 19, 2018
Discovered a weird bug where compiling > 9 .less files was always produced .css file short.
Traced it down to this function where it was deleting a compiled .css file instead of the intended `mix.js`.
Full details here: laravel-mix#1420
@JeffreyWay
Copy link
Collaborator

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants