Laravel-mix: [1.*] Webpack code splitting - wrong directory

Created on 26 Jun 2017  ยท  36Comments  ยท  Source: JeffreyWay/laravel-mix

  • Laravel Mix Version: 1.0.6
  • Node Version: 8.1.0
  • NPM Version: 5.0.3
  • Yarn Version: 0.21.3
  • OS: Windows 10

Description:

With Laravel Mix 0.* modules created with webpack code splitting were automatically put in the public/js directory. Now, with Mix 1.* it puts all the chunks in the public directory(unless for each chunk I specify its name with js/ prefix).

Steps To Reproduce:

  • $ laravel new foo --dev
  • Update Mix version (0.* to 1.*) in package.json
  • $ yarn
  • $ yarn add babel-preset-es2015 babel-preset-stage-2 (not sure here which one gives the ability to import dynamically)
  • Create .babelrc file:
{
  "presets": ["es2015", "stage-2"]
}
  • In app.js split the Example component to a separate chunk
// Replace this
Vue.component('example', require('./components/Example.vue'));
// With this
Vue.component('example', () => import('./components/Example.vue'));
  • $ yarn run development

The 0.js file should appear in public directory.

Possible fix:

In webpack.mix.js add

mix.webpackConfig({
    output: {
        chunkFilename: 'js/[name].[chunkhash].js',
    },
});

Is there a better way to fix this?

stale

Most helpful comment

This worked for me:

mix.webpackConfig({
    output: {
        chunkFilename: 'js/[name].js',
    }
});

All 36 comments

I can confirm this behavior.
Path behavior seems to be broken (or at least very different) in 1.x. This might be related to #923 and #924

@jbbr I'm not sure it is related. Previously, in 0.*, when I dumped the webpack config, the chunkFilename was set to:

'js/[name].[chunkhash].js'

It was probably removed by mistake.

okay, not sure. I'll try to fix this and the publicPath issue I experience once I find the time.

If anyone wants to try a fix here urls to the files which generate the chunkFilename:
Relevant line in 1.12.x: https://github.com/JeffreyWay/laravel-mix/blob/712d4daf07468177eaef4d7f95672513550e914d/src/Mix.js#L59
Relevant line in 1.0.7: https://github.com/JeffreyWay/laravel-mix/blob/1da78b6ea0c1b256c582f5dbdea9b14802699e50/src/builder/WebpackConfig.js#L67

Yeah, the 1.* version is missing the base path for chunk names, which was previously derived from the script output path. If the compilation was defined as follows:

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

global.entry.base was public/js, output.chunkFilename was public/js/[name].[chunkhash].js and everything worked correctly.
@JeffreyWay Should I make a PR fixing this?


Also:

Relevant line in 1.12.x:

I think you meant 0.12.x

I believe I'm also having this problem as well. Using WordPress, which has its assets in /wp-content/themes/[theme name], so I need to set the publicPath.

Oddly, when I tried to manually update it on https://github.com/JeffreyWay/laravel-mix/blob/1da78b6ea0c1b256c582f5dbdea9b14802699e50/src/builder/WebpackConfig.js#L67 as mentioned above, it broke the compliation. Seems other parts of the script try to reference the publicPath for the actual file instead of just the path?

Ignore that second paragraph, I must have changed another path setting without realizing it.

@jon-heller I believe you can use webpackModuleName magic comment to point to the right directory if you want to do this only a couple of times.

Well, I'm confused. Went back and forth a few times but this does seem to be working, and updating the publicPath correctly where it uses this directory as the base URL when looking for chunks.

.webpackConfig({
        output: {
            publicPath: '/wp-content/themes/theme-name/',
            chunkFilename: 'assets/dist/[name].[chunkhash].js',
        },
    })

I'm able to split my JS file using System.import instead of require.
This works perfectly when running devor productionfrom npm. But this is not working on run watch.

On file update, I see npmmaking stuff, and outputing splitted file inpublic/js folder. But I think that because of the changing chunkhash, the JS file linked in front is the wrong one: still the old (from first pass of npm run watch).

If i remove the chunkhashfrom chunkFilenameparameter, it works, as the file is overrided without any hash in filename; but i loose the browser caching invalidation as filename/url is the same, and mix.version() seems to not apply on those files.

This worked for me:

mix.webpackConfig({
    output: {
        chunkFilename: 'js/[name].js',
    }
});

@silvioiannone yes, but dont you loose browser header cache invalidation ? As filename is always the same, and mix.version() doesn't seems to apply on chunked files ?

I finally made a condition "if production" to use chunkFilenames with chunkHash, and without chunkhash for production build. This way, i have my watch working, and my fillename have hashes on production.

@mtx-z You're right, didn't think about that. But at the moment we're still working on our project and hopefully this issue will be solved before going live.

All right, don't forget it !
In the meantime, this is how i managed it;

const {mix} = require('laravel-mix');
if (mix.inProduction()) {
    mix.webpackConfig(... chunkFilename: 'js/[name].[chunkhash].app.js...);
} else {
    mix.webpackConfig(...chunkFilename: 'js/[name].app.js'...);
}

I personally always use versioning. It doesn't give much overhead and it saves me from forgetting to apply the versioning when releasing the app.

How do you make versionning work fur chunked files ?

As I stated previously:

mix.webpackConfig({
    output: {
        chunkFilename: 'js/[name].[chunkhash].js',
    },
});

I haven't had any issues with this approach. Are you having any problems?

@KKSzymanowski I switched to your solution so it includes the chunkhash too now, thanks :)

@KKSzymanowski I'll test. So your solution also work using "npm watch" ? Good chunk hash filename is loaded on front after watch detected a change and built ?

@mtx-z It works but you end up with multiple versions of the same chunk:

Result screenshot

Basically every time a chunk is re-built the old one is not deleted.

@silvioiannone I have the same, but on the frontend side, after you refreshed your page, does the frontend have the latest file linked into the DOM ? As for me, only the first one created after i ran the npm watch is used. Other files with new chunk are created, but never linked on frontend.

Basically every time a chunk is re-built the old one is not deleted.
Same here.

@mtx-z Yes, I just made the test because I was unsure and it appears the the newest chunks are always linked.

I'm no expert in webpack but from what I understand Laravel only provides the main chunk and all others are linked by webpack so you should always get the correct version.

@silvioiannone hum ok. Strange as here I always get the first file (so first chunkhash filename) linked, new ones are never used until a new npm watch/dev/prod. I'll do more test ! Thanks a lot for your testings.

@silvioiannone @KKSzymanowski I just tested again.

My Webpach have filename: '[name].js', chunkFilename: 'js/[name].[chunkhash].js'

  • npm watch generate this file 37.c18fba44997ac048aa8e.js
  • the file is linked in frontend
  • now i edit the related compenent
  • npm is detecting watch, building and generate 37.0fb2c17e4dabf1e798ee.js, first file is not deleted and still present.
  • refresh browser (without cache checked)
  • first file 37.c18fba44997ac048aa8e.js is still linked to DOM, updates have made are so not visible.

@mtx-z What version of Laravel-Mix do you have exactly? Mine is 1.2.2.

^1.0.6 for me. I try update.

Now at 1.2.2 (had to follow this), and still the same...

I can confirm this in 1.4.2

it would be awesome if this could work with query strings!

In the meantime, @mtx-z's solution seems to work alright

Hi there,

I'm having some troubles with this too, I'm using a different path for my public path when running on production, the chunks are generated correctly on their path but on the browser in the production site is loading from my development path. Here you have an example:

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

if (mix.inProduction()) {
    mix.setPublicPath('public/build');
}

mix.webpackConfig({
    output: {
        chunkFilename: mix.inProduction() ? 'chunks/[name].[chunkhash].js' : 'chunks/[name].js'
    }
}

Chunks generated for development:
public/chunks/0.0.js
public/chunks/1.1.js

Chunks generated for production:
public/build/chunks/0.390c52103f7e07f649e0.js
public/build/chunks/1.72a4a93750363d33617e.js

So far so good. Now in production website when the chunks are loaded they load from:
/chunks/0.390c52103f7e07f649e0.js <-- not found
/chunks/1.72a4a93750363d33617e <-- not found

... instead from:
/build/chunks/0.390c52103f7e07f649e0.js
/build/chunks/1.72a4a93750363d33617e.js

Am I missing something?

@geodeveloper How about

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

if (mix.inProduction()) {
    mix.setPublicPath('public/build');
}

mix.webpackConfig({
    output: {
--      chunkFilename: mix.inProduction() ? 'chunks/[name].[chunkhash].js' : 'chunks/[name].js'
++      chunkFilename: mix.inProduction() ? 'build/chunks/[name].[chunkhash].js' : 'chunks/[name].js'
    }
}

@KKSzymanowski thanks for the reply. I tried your solution but still now working properly, it generates a new nested build folder for the chunks, like this:

-- public
  -- build
    -- build
       -- chunks
         -- 0.390c52103f7e07f649e0.js
         -- 1.72a4a93750363d33617e.js

Tha's because my build folder is set with mix.setPublicPath('public/build'); so everything is generated within that folder.

I'm confused.
In development you want following structure:

โ””โ”€ public
   โ”œโ”€ chunks
   โ”‚  โ”œโ”€ 0.js
   โ”‚  โ””โ”€ 1.js
   โ”œโ”€ js
   โ”‚  โ””โ”€ app.js
   โ””โ”€ mix-manifest.json

And in production:

โ””โ”€ public
   โ”œโ”€ build
   โ”‚  โ””โ”€ chunks
   โ”‚     โ”œโ”€ 0.390c52103f7e07f649e0.js
   โ”‚     โ””โ”€ 1.72a4a93750363d33617e.js
   โ”œโ”€ js
   โ”‚  โ””โ”€ app.js
   โ””โ”€ mix-manifest.json

Am I correct?

Ok, I got it working. I needed to specify where I have the public files in webpack configuration, by adding publicPath. Now it seems it loads the chunks correctly from /build/chunks/ on production site:

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

if (mix.inProduction()) {
    mix.setPublicPath('public/build');
}

mix.webpackConfig({
    output: {
         chunkFilename: mix.inProduction() ? 'chunks/[name].[chunkhash].js' : 'chunks/[name].js',
         publicPath: mix.inProduction() ? '/build/' : '/'
    }
}

Thanks @KKSzymanowski for your time and input, I really appreciate!

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

For anyone still dealing with this, the easiest thing I've found is to _simply let_ the js chunk files float around in the root /public directory, you can ignore them from git via the following lines added to your .gitignore:

/public/*.js
/public/*.js.map

Related to https://github.com/JeffreyWay/laravel-mix/issues/1889
In the meantime, commenting out mix.extract() helped me (v4.0.15)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sdebacker picture sdebacker  ยท  3Comments

rderimay picture rderimay  ยท  3Comments

nezaboravi picture nezaboravi  ยท  3Comments

kpilard picture kpilard  ยท  3Comments

stefensuhat picture stefensuhat  ยท  3Comments