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

Honor the public path by removing "/" from generated CSS and JS files #1717

Closed
mislam opened this issue Jul 14, 2018 · 20 comments · May be fixed by #3280
Closed

Honor the public path by removing "/" from generated CSS and JS files #1717

mislam opened this issue Jul 14, 2018 · 20 comments · May be fixed by #3280
Labels

Comments

@mislam
Copy link

mislam commented Jul 14, 2018

  • Laravel Mix Version: 2.1.11 (npm list --depth=0)
  • Laravel Version: 5.4
  • Node Version (node -v): v8.11.3
  • NPM Version (npm -v): 6.1.0
  • OS: macOS High Sierra (10.13.5), Homestead 7, Ubuntu 18.04 LTS

Reference:

This issue was also mentioned in Workbox repository:

GoogleChrome/workbox#1534

Description:

Up until v1.7.2, Laravel-Mix was outputting the js/app.js and css/app.css with a forward slash like:

/js/app.js
/css/app.css

When upgraded to v2.1.11, I noticed a double slash in the path:

//js/app.js
//css/app.css

This ends up resulting these two files to be loaded from http://js/app.js and http://css/app.css which results a 404 error.

However, this works fine for anyone running out-of-the box installation of Laravel. But when the application starts growing and you want to integrate a service worker plugin (i.e. Workbox), then the real monster comes to play (see the Steps To Reproduce section below).

This is only because Laravel-Mix isn't honoring the public path in the first place. Regardless of v1.7.2 or v2.1.11, it shouldn't set the path to "/" after compiling the scripts and sass files. It should rather set the paths relative, such as: "js/app.js" and "css/app.css" instead of "/js/app.js" and "/css/app.css".

I noticed the fonts and images aren't using this leading "/" while outputting them in the public directory. Only the js and css files are including it.

To understand what's being generated from the Workbox plugin, take a look at my precache manifest file generated by the workbox plugin (refer to the Steps To Reproduce section below) while using Laravel Mix v1.7.2:

self.__precacheManifest = [
  {
    "revision": "c1deb96084cecef66f0e1f41e7fe1c96",
    "url": "apple-touch-icon.png"
  },
  {
    "revision": "44f2b77fa6a419081a6271801e5fda28",
    "url": "img/svg/magnifier.svg"
  },
  {
    "revision": "01232e8fd32ab1e8997e",
    "url": "/js/vendor.js"
  },
  {
    "revision": "b72a3712fdabc4d2cc6b138e00ae1e96",
    "url": "img/logo-dark.png"
  },
  {
    "revision": "598b07f2fce824854ebe0bba82b2b397",
    "url": "img/logo-light-small.png"
  },
  {
    "revision": "e8c13486745f6a1c3fd3cc7788eaace2",
    "url": "img/logo-light.png"
  },
  {
    "revision": "0d22b57cc0113eaf5fff48fe2339e738",
    "url": "fonts/icomoon.eot"
  },
  {
    "revision": "ac7e3b54ed37c5dc26bfdbd2ff9a2e83",
    "url": "fonts/icomoon.ttf"
  },
  {
    "revision": "a09c96d925ad889a9fe2169fa977300d",
    "url": "fonts/icomoon.woff"
  },
  {
    "revision": "06dbce2d2e8fc4cafa42319937955521",
    "url": "fonts/icomoon.svg"
  },
  {
    "revision": "3590030f106aae5b66d36fcbd03b2bf0",
    "url": "favicon-16x16.png"
  },
  {
    "revision": "c2d8bfb9b66a4bb17a1e71757b5ee13a",
    "url": "favicon-32x32.png"
  },
  {
    "revision": "7ca2abdc7b1ec65f45104eea49e92b63",
    "url": "favicon.ico"
  },
  {
    "revision": "38d143e25f32fb48f698",
    "url": "/js/manifest.js"
  },
  {
    "revision": "7a9765e1523cee9eed8f",
    "url": "/js/app.js"
  },
  {
    "revision": "7a9765e1523cee9eed8f",
    "url": "/css/vendor.css"
  },
  {
    "revision": "7a9765e1523cee9eed8f",
    "url": "/css/app.css"
  }
];

This worked for me until the forward slashes doubled in Laravel Mix v2.1.11 only for js and css files:

[
  ...
  {
    "revision": "7a9765e1523cee9eed8f",
    "url": "//js/app.js"
  },
  {
    "revision": "7a9765e1523cee9eed8f",
    "url": "//css/vendor.css"
  },
  {
    "revision": "7a9765e1523cee9eed8f",
    "url": "//css/app.css"
  }
  ...
]

If Laravel Mix returns the JS and CSS file paths without any leading slash, that will solve this issue. I hope @JeffreyWay will look into this.

Steps To Reproduce:

Install the workbox-webpack-plugin and then update the webpack.mix.js file to use it for generating the service worker file:

const WorkboxPlugin = require('workbox-webpack-plugin')

...
mix.js(...)
   .sass(...)
   .webpackConfig({
       ...
       plugins: [
           ...
           // Generate service worker
           new WorkboxPlugin.GenerateSW({
               swDest: 'service-worker.js',
               clientsClaim: true,
               skipWaiting: true
           })
       ]
   })
@gdebrauwer
Copy link

gdebrauwer commented Jul 18, 2018

I'm having the same problem in laravel mix 2.1.

This is problematic if you want to insert script tags into a html using html webpack plugin.
Css files don't seem to have that same problem.

I tried laravel mix 2.0 and the issue is not present in that version.

Can someone help?

my webpack.mix.js code:

const mix = require('laravel-mix');
const HtmlWebpackPlugin = require('html-webpack-plugin');

mix.setPublicPath('dist');

mix.webpackConfig({
  plugins: [
    new HtmlWebpackPlugin({
      template: 'public/index.html',
      filename: 'index.html',
    }),
  ],
});

mix.sass('./src/test.scss', '.css/main.css'); // generates '/css.main.css' in stylesheet link in index.html
mix.js('./src/main.js', 'js/main.js') // generates '//js/main.js' in script tags in index.html. why?
  .extract([
    'vue',
    'vue-router',
    'vuex',
  ]);

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

output in terminal: (the js file uri's should not start with a '/')

images/logo.png?250511dbe7e64107809d0e6d980ff135    5.19 kB          [emitted]
                                     /js/main.js     104 kB       0  [emitted]         /js/main
                                   /js/vendor.js    1.08 MB       1  [emitted]  [big]  /js/vendor
                                 /js/manifest.js    3.84 kB       2  [emitted]         /js/manifest
                                    css/main.css   78 bytes    0, 0  [emitted]         /js/main, /js/main
                                      index.html

EDIT:
my remark on terminal output is not correct. version 2.0 als show /js/main.js and that works (it does not use double slashes in plugins as in v2.1)

@philicevic
Copy link

philicevic commented Oct 17, 2018

Having the same issue with laravel mix 2.1.14.

I am trying to inject my script via the html-webpack-plugin, but i end up with //js/main.js as src, while the path to my css file looks just fine.

I did try to manipulate the webpack config just to see whats going on and found out that the entry file (which should be js/main.js) already has a leading slash, which shouldn't be there. If I manually change that value on runtime I get the right result.

@JeffreyWay It would be nice to know if the leading slash is intentional. At the moment it just causes problems, at least for me..

Steps To Reproduce

I am using a basic mix setup with a self written plugin called 'mix-html-builder' as an additional dependency.

webpack.mix.js

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

require('mix-html-builder');

mix.setPublicPath('out')
   .js('src/js/main.js', 'js')
   .sass('src/scss/main.scss', 'css')
   .buildHtml('src/index.html', 'index.html', 'src/partials');

In out/index.html the injected script tag looks like this:

<script type="text/javascript" src="//js/main.js"></script>

And as I said, the path of my css file looks fine.

@arubacao
Copy link

I'm having the exact same problem as @philicevic .
Did you guys find a work-around or how did you solve the problem?

@tyler36
Copy link

tyler36 commented Nov 20, 2018

As a hacky workaround, I've added the following to my webpack.mix.js

const replace = require( 'replace-in-file' );
const path = require( 'path' );
const publicDir = 'public/html';
...
mix.js( 'resources/js/app.js', 'js' )
 ...
        .then( () => replace.sync( {
            // FIXME:   Workaround for laravel-mix placeing '//*.js' at the begining of JS filesystem
            files: path.normalize( `${publicDir}/precache-manifest.js` ),
            from:  /\/\//gu,
            to:    '/',
        } ) )

Not too proud of the solution but it works. Hopefully now that @JeffreyWay has finished his site updates, he'll have a bit of time to take a look.

@hlorofos
Copy link

is there any way to apply versions from mix-manifest.json to the files referred from html?

@donmbelembe
Copy link

Any update about this issue ?

@donmbelembe
Copy link

@tyler36 your workaround doesn't work for me because precache-manifest file name contain a unique hash precache-manifest.79ad89616b48f59453b7ebaa4d7ca623.js

@tyler36
Copy link

tyler36 commented Jan 21, 2019

@donmbelembe mine doesn't have a hash.
I'm not that familiar with replace-in-file but it might accept a globs for the filename, then you could do something like

...
files: path.normalize( `${publicDir}/precache-manifest.*.js` ),
...

@acacha
Copy link

acacha commented Feb 22, 2019

@JeffreyWay ping

@butaminas
Copy link

Having same problem. Furthermore, if I include chunk in the file name it also adds it to app.js and vendor.js files and then I can't use those files in index blade on Laravel.

@stale
Copy link

stale bot commented May 21, 2019

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.

@stale stale bot added the stale label May 21, 2019
@stale stale bot closed this as completed May 24, 2019
@ghost
Copy link

ghost commented Sep 5, 2019

Hi for anyone still having this issue. I could fix the problem by manually overwriting the publicPath setting like this:

mix.webpackConfig({
    output: {
        publicPath: "",
    },
});

Be sure to apply the right publicPath for your setup!
(tested with workbox 5.0.0-alpha.1)

@lorisleiva
Copy link

lorisleiva commented Sep 24, 2019

Same problem here but setting up the publicPath to nothing caused some errors on my chunk files.

Digging into the core of Laravel mix, I found out that there is an unnecessary leading slash in the entry array.

  "entry": {
    "/main": [
      "/Users/lorisleiva/projects/acme/src/main.js"
    ]
  },

This is fixed by chaining another replace call to the createName method of the Entry class in order to remove any leading slash when generating a new entry.

  // src/builder/Entry.js

  createName(output) {
    let name = output
      .pathFromPublic(Config.publicPath)
      .replace(/\.js$/, "")
      .replace(/\\/g, "/")
      .replace(/^\//, ""); // <- Adding this line makes it work.

    this.base = path.parse(name).dir;

    return name;
  }

This now outputs:

  "entry": {
    "main": [
      "/Users/lorisleiva/projects/acme/src/main.js"
    ]
  },

I'm happy to make a PR for this but I have no ideas if this is going to break something for non-standalone application.

Edit: In the meantime, you can add this override function to your mix file.

mix.override(config => {
  config.entry = Object.keys(config.entry).reduce((acc, key) => {
    acc[key.replace(/^\//, "")] = config.entry[key];
    return acc;
  }, {});
});

@nat-mystudiosessions
Copy link

@lorisleiva Thanks so much for sharing. I would certainly support a PR or fork. I have the same issue where setting publicPath empty screws up my chunk files. Pretty frustrating.

When I applied your suggestion it solved the problem slash in front of my js file "/js/app.js" but I still have a slash in front of my css file "//css/app.css". Just curious if you had a similar issue with css, and if so can you share how you got around it? Thanks.

@lorisleiva
Copy link

Argh sorry to hear that. No I’m afraid on that particular project I’ve got all my CSS defined within single file components in Vue.js. The code I pasted above should remove the trailing slashes of all the keys in the entry configuration. I’m not sure where else a trailing slash would be added for CSS. Maybe if you do a mix.dump() you could find it. I hope this helps.

@pmochine
Copy link

@lorisleiva for me the override function won't work. I use mix 3.0. I'm stuck because I cannot update because of the empty css file bug...

@gtempesta
Copy link

gtempesta commented Nov 9, 2020

Thanks @lorisleiva your solution is working in my case. Because of this problem I had to add /js by hand to every dynamic import and to the configuration of the vendor and manifest files, now I can just add it to the names in webpackConfig without having the double slashes in the file name:

.webpackConfig({
  output: {
    filename: 'js/[name].js',
    chunkFilename: 'js/[name].js?id=[chunkhash]',
    path: path.resolve(__dirname, 'public'),
  },
})

As an addition to the discussion, I've seen that the slash appeared also under Chunk Names in the logs, and it disappeared after the fix.

@glensc
Copy link

glensc commented Nov 9, 2020

As a solution, I've added webpack.mix.js transform function in my project:

Altho I'd like to see a solution from Symfony/Asset side, I've updated on the Mix side to strip prefix from items.

const collect = require('collect.js');

/**
 * Update manifest to remove leading slash of key => value pairs
 * @author Elan Ruusamäe <glen@pld-linux.org>
 * @see https://github.com/symfony/symfony/issues/36234
 */
mix.extend('updateManifestPathsRelative', (config) => {
    config.plugins.push(new class {
        apply(compiler) {
            compiler.plugin('done', () => {
                const manifest = {};
                collect(Mix.manifest.get()).each((value, key) => {
                    key = this.normalizePath(key);
                    value = this.normalizePath(value);
                    manifest[key] = value;
                });
                Mix.manifest.manifest = manifest;
                Mix.manifest.refresh();
            });
        }

        /**
         * @param {string} filePath
         */
        normalizePath(filePath) {
            if (filePath.startsWith('/')) {
                filePath = filePath.substring(1);
            }

            return filePath;
        }
    })
});

mix.updateManifestPathsRelative();

refs:

@mrleblanc101
Copy link

mrleblanc101 commented Jun 14, 2022

This seem to be related to this: #2862.
This is still happening in Laravel Mix 6, but only with JS files.

@mrleblanc101
Copy link

I'll open a PR with @lorisleiva fix. Hope this get merged.

mrleblanc101 pushed a commit to mrleblanc101/laravel-mix that referenced this issue Jun 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.