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

less-loader is trying to resolve url references relative to the starting point(root less file) instead of using the current file #76

Closed
ravipatipushkar opened this issue Feb 8, 2016 · 19 comments

Comments

@ravipatipushkar
Copy link

I am getting the below error when trying to use the less loader with webpack to compile my less files.

ERROR in ./~/css-loader?sourceMap!./~/less-loader?relativeUrls&noIeCompat!./app/styles/app.less
    Module not found: Error: Cannot resolve 'file' or 'directory' ./fonts/randomfont.eot in C:\test\app\styles
     @ ./~/css-loader?sourceMap!./~/less-loader?relativeUrls&noIeCompat!./app/styles/app.less 6:92-125

Following is my directory structure

---Test
    |---App
    |     |---styles
    |     |       |---fontImport
    |     |       |      |---randomfont.eot
    |     |       |---app.less
    |     |---  app.js             
    |---webpack.config.js

Following are the contents of
app.js
require('./styles/app.less');

app.less

@import "./fontImport/fontimports.less";

body{
  background: #fafafa;
  color: #212121;
  padding: 0;
  margin: 30;
  font-size: 60px;
}

fontImports.less

@font-face {
    font-family: 'Times New Roman';
    // Does not work
        src: ~"url('./fonts/randomfont.eot')";
    // Works
    //src: ~"url('./fontImport/fonts/randomfont.eot')";
    font-weight: normal;
    font-style: normal
}

I am using the below webpack config

var path = require('path');
var extractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
    entry: './app/app.js',
    devtool: 'sourcemap',
    output: {
        filename: 'test.js',
        library: 'testlib',
        libraryTarget: 'var',
        path: './Content/Scripts',
        pathinfo: true
    },
    resolve: {
        extensions: ['', '.webpack.js', '.web.js', '.ts', '.tsx', '.js']
    },
    module: {
        loaders: [
            { test: /\.css$/, loader: "style!css" },
            {
                test: /\.less/,
                loader: extractTextPlugin.extract(
                    "css?sourceMap!" +
                    "less?relativeUrls&noIeCompat"
                    )
            },
            { test: /\.(eot|woff|woff2|ttf|svg|png|jpg)$/, loader: 'file?name=app/generated/[name].[ext]' },
        ]
    },
    plugins: [
        new extractTextPlugin("../css/styles.css"),
    ]
}

In fontImports.less, If I provide relative path of the font form fontImports.less, webpack command is failing with the below error.

Hash: 1c54ca6132cc3c5db535
Version: webpack 1.12.13
Time: 1313ms
      Asset     Size  Chunks             Chunk Names
    test.js  4.68 kB       0  [emitted]  main
test.js.map  5.28 kB       0  [emitted]  main
   [0] ./app/app.js 31 bytes {0} [built]
    + 3 hidden modules

ERROR in ./app/styles/app.less
Module not found: Error: Cannot resolve 'file' or 'directory' ./fonts/randomfont.eot in C:\test\app\styles
 @ ./app/styles/app.less 6:90-123

ERROR in ./app/styles/app.less
Module build failed: ModuleNotFoundError: Module not found: Error: Cannot resolve 'file' or 'directory' ./fonts/randomfont.eot in C:\test\app\styles
    at C:\test\node_modules\webpack\lib\Compilation.js:229:38
    at onDoneResolving (C:\test\node_modules\webpack\lib\NormalModuleFactory.js:29:20)
    at C:\test\node_modules\webpack\lib\NormalModuleFactory.js:85:20
    at C:\test\node_modules\async\lib\async.js:726:13
    at C:\test\node_modules\async\lib\async.js:52:16
    at done (C:\test\node_modules\async\lib\async.js:241:17)
    at C:\test\node_modules\async\lib\async.js:44:16
    at C:\test\node_modules\async\lib\async.js:723:17
    at C:\test\node_modules\async\lib\async.js:167:37
    at C:\test\node_modules\enhanced-resolve\lib\UnsafeCachePlugin.js:24:19
    at onResolved (C:\test\node_modules\enhanced-resolve\lib\Resolver.js:38:18)
    at C:\test\node_modules\enhanced-resolve\lib\Resolver.js:127:10
    at C:\test\node_modules\enhanced-resolve\lib\Resolver.js:191:15
    at applyPluginsParallelBailResult.createInnerCallback.log (C:\test\node_modules\enhanced-resolve\lib\Resolver.js:110:4)
    at loggingCallbackWrapper (C:\test\node_modules\enhanced-resolve\lib\createInnerCallback.js:21:19)
    at C:\test\node_modules\tapable\lib\Tapable.js:134:6
    at Tapable.<anonymous> (C:\test\node_modules\enhanced-resolve\lib\DirectoryDefaultFilePlugin.js:21:12)
    at Storage.provide (C:\test\node_modules\enhanced-resolve\lib\CachedInputFileSystem.js:52:20)
    at CachedInputFileSystem.stat (C:\test\node_modules\enhanced-resolve\lib\CachedInputFileSystem.js:132:20)
    at Tapable.<anonymous> (C:\test\node_modules\enhanced-resolve\lib\DirectoryDefaultFilePlugin.js:18:6)
    at Tapable.applyPluginsParallelBailResult (C:\test\node_modules\tapable\lib\Tapable.js:139:14)
    at Tapable.<anonymous> (C:\test\node_modules\enhanced-resolve\lib\Resolver.js:103:8)
    at Tapable.Resolver.forEachBail (C:\test\node_modules\enhanced-resolve\lib\Resolver.js:196:3)
    at Tapable.doResolve (C:\test\node_modules\enhanced-resolve\lib\Resolver.js:102:7)
    at Tapable.resolve (C:\test\node_modules\enhanced-resolve\lib\Resolver.js:45:14)
    at Tapable.resolve (C:\test\node_modules\enhanced-resolve\lib\UnsafeCachePlugin.js:23:14)
    at C:\test\node_modules\webpack\lib\NormalModuleFactory.js:82:29
    at C:\test\node_modules\async\lib\async.js:718:13
    at async.forEachOf.async.eachOf (C:\test\node_modules\async\lib\async.js:233:13)
    at _parallel (C:\test\node_modules\async\lib\async.js:717:9)
Child extract-text-webpack-plugin:
        + 3 hidden modules

    ERROR in ./~/css-loader?sourceMap!./~/less-loader?relativeUrls&noIeCompat!./app/styles/app.less
    Module not found: Error: Cannot resolve 'file' or 'directory' ./fonts/randomfont.eot in C:\test\app\styles
     @ ./~/css-loader?sourceMap!./~/less-loader?relativeUrls&noIeCompat!./app/styles/app.less 6:90-123

If I provide the relative path from app.less, I am getting the expected result. Do I have to include any specific configuration to make the above work ?

You can repro this issue using the sample here

@duro
Copy link

duro commented Mar 14, 2016

I can confirm this is a problem. I am running into the exact same issue.

@oller
Copy link

oller commented Mar 14, 2016

I'm encountering this problem too, particularly when trying to import the .less flag library from the node_module https://github.com/lipis/flag-icon-css

In my less file for that module

@import "~flag-icon-css/less/flag-icon.less";

This resolves fine, and webpack starts importing this file. The problem is then within the flag node package, the relative paths to the individual flag svg files ../flags/1x1/es.svg look like they're being resolved relative to starting point of the less import into webpack, i.e. styles/app.less and not relative to the node_modules package.

The error, excuse all the loaders...

    ERROR in ./~/css-loader?sourceMap!./~/postcss-loader!./~/csslint-loader!./~/less-loader?sourceMap!./app/styles/profiler.less
    Module not found: Error: Cannot resolve 'file' or 'directory' ../flags/1x1/vg.svg in /app/styles
     @ ./~/css-loader?sourceMap!./~/postcss-loader!./~/csslint-loader!./~/less-loader?sourceMap!./app/styles/app.less 6:116980-117010

@jhnns
Copy link
Member

jhnns commented Mar 25, 2016

This looks like the relativeUrls option is not applied. The less-loader usually applies it automatically, thus it should work out of the box. Could you remove the relativeUrls option in your loader config and see if it works?

@developit
Copy link

@jhnns Tried with relativeUrls true or false, same issue. This seems related to the way the file manager provided by the dynamically injected less plugin uses the shared context from less-loader to resolve paths for url() references within all nested files. I'm not familiar enough with Webpack's internals to know how to split this out.

Is there a way to re-apply less-loader to each new less file encountered?

@jhnns
Copy link
Member

jhnns commented Apr 19, 2016

We need a small test-case that demonstrates this specific bug. We do have tests that this feature works under "normal" circumstances (I'm using the less-loader also in production and it works)

@developit
Copy link

@jhnns Yes, I have used less-loader many times without issue. I ran into this when (intentionally) attempting to apply less-loader to modules in node_modules. In particular, it happened when using namespaced NPM module names (@foo/bar).

That being said, I was actually able to fix my issue by simply dropping the relative filepath altogether. It still bundles the font properly via webpack, but seems to circumvent the path normalization based on original filename somehow.

src: url('./icons/foo.woff') format('woff');
src: url('/icons/foo.woff') format('woff');

@emmostrom
Copy link

I ran into this issue because the way I set up my webpack.config.js file - the paths were getting a mixture of forward and backwards slashes (context: __dirname + '/src/main/jsx').

Changed to use path.join and path.normalize

context: path.join(__dirname, '/src/main/jsx'),
...
output: {
path: __dirname,
filename: path.normalize('/src/main/webapp/js/app.js')
},
...
new ExtractTextPlugin(path.normalize('src/main/webapp/css/app.css'))

This stopped errors but no files were generated so also had to update to node 4.4.4

@tszymanek
Copy link

tszymanek commented May 18, 2016

I had a similar issue and solved it in a hackish way:

http://stackoverflow.com/a/37293335/1874624

edit: updated my solution

@SplicePHP
Copy link

I found the problem. Since most less and scss files are stuck in other directories than where they build to, the css parser gets confused. I added an attribute to the css loader named rel to specify where the file would built to.
Here is the modified loader: https://github.com/SplicePHP/css-loader

Example usage:

    module: {
        loaders: [ //default less loader
            {
                test: /\.less$/,
                loader: ["style-loader", "css-loader","less-loader"],
                exclude: [ //exclude package in order not to get loaded via this loader
                    /\package\/less\/style.less$/
                ]
            },
            { //created custom loader to fix wrong rel path issue
                test: /\package\/less\/style.less$/,
                loader: [
                    "style-loader",
                    "css-loader?rel="+path.resolve(__dirname,'path/to/package/dist/css'),
                    "less-loader"
                ]
            }
        ]
    }

I am not 100% sure that the regex is best practice for detecting relative urls, just pulled it from stack overflow.
Works as far as I have tested. I tried to submit a pull request but I don't think it got through. If I don't hear anything by tomorrow I will resubmit the pull request to the css-loader git repo.

Not sure if I will be maintaining this fork, depends if another solution is found.

@jhnns
Copy link
Member

jhnns commented May 19, 2016

Sorry guys, this feature is working under "normal" (=expected) circumstances. Please give us a minimal example so we can investigate this.

@SplicePHP
Copy link

SplicePHP commented May 20, 2016

Hi, here is an example repo that results in build errors:
https://github.com/almasaeed2010/AdminLTE.git

npm:

npm install admin-lte --save-dev

entry.js

require('admin-lte/build/less/AdminLTE.less');

Webpack Config
[I trimmed a lot of code out of my webpack config but here is the relevant code to illustrate the problem: have not tested this but I think you should get the idea. ]

var path = require('path');
var webpack = require('webpack');

module.exports = {
    entry: path.resolve(__dirname, "entry.js"),
    context: path.resolve(__dirname),
    output:
    {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js',
        chunkFilename: "[name]_[chunkhash:20].js",
        sourceMapFilename: "[name].map"
    },
    module:
    {
        loaders: [
            {
                test: /\.js$/,
                loader: "babel-loader",
                query:
                {
                    presets: ['es2015', 'react', 'stage-0']
                }
            },
            {
                test: /\.css$/,
                loader: ["style-loader", "css-loader"]
            },
            {
                test: /\.less$/,
                loader: ["style-loader", "css-loader", "less-loader"],
                exclude: [
                    // /\less\/AdminLTE.less$/
                ]
            },
            // {
            //     test: /\less\/AdminLTE.less$/,
            //     loader: [
            //         "style-loader",
            //         "css-loader?rel="+path.resolve(__dirname,'node_modules/admin-lte/dist/css'),
            //         "less-loader"
            //     ]
            // },
            {
                /*
                 * embed images and fonts smaller than 5kb
                 * 'image-webpack-loader?optimizationLevel=7&..........'
                 */
                test: /\.(gif|png|jpg|jpeg|svg)($|\?)/,
                loaders: ['url?limit=5000&hash=sha512&digest=hex&size=16&name=resources/[name]-[hash].[ext]']
            },
            {
                test: /\.(woff|woff2|eot|ttf)($|\?)/,
                loaders: ['url?limit=5000&hash=sha512&digest=hex&size=16&name=resources/[name]-[hash].[ext]']
            }
        ]
    }
};

I commented out the code I currently use to fix the issue.

So this is what happens:
You include the less file from the admin-lte/build/less/ dir.
When it hits the css loader it thinks it is in admin-lte/build/less/ and can't find relative resources.
All the resources lay inside the admin-lte/dist/ dir.
The less file is supposed to built to admin-lte/dist/css/
Now any resources required like: url(../img/someimage.jpg) will try to load admin-lte/build/img/someimage.jpg instead of admin-lte/dist/img/someimage.jpg
The solution I propose is to make allowance for the build output dir to be specified in some way in order for the relative paths to load correctly in these cases.
I used the rel keyword in my example.
You can see an example of my hacked css-loader in the link supplied in the previous post.

Thanks.

@jhnns
Copy link
Member

jhnns commented May 23, 2016

Thanks for providing an example @SplicePHP

Your assumptions are correct, but this is the expected behavior. The less-loader generates a single css file from multiple less files. That's why the css-loader tries to resolve all urls relatively from the less entry file.

When using webpack, it's best to put your binary assets like images and fonts inside your app or src or build folder (whatever you call it). Then you just reference them in your less file relatively like you would with any other JS import/require and things will work as expected. So, you don't need any path variable in your less files, because if you reference them relatively, things will just work.

@SplicePHP
Copy link

The problem is that most people are not in control of those variables. I am working on a pretty large project with a ton of node_modules and other git libraries. The standard is for less and scss files to be stored in another path to where the actual build is rendered to. Making these types of changes on third party libraries will be a nightmare for version control. I would have to fork nearly every git repo I use to modify the paths and I am not even sure that version control can be maintained with npm packages if these types of changes are made locally. This is a serious impediment for maintainability as far as I can tell, Stack Overflow is full of these bugs and no-one seems to have any answers. It is much easier to add a rel path variable to the css loader and no hacking of third party libraries is required. Making this small change fixes the problem and no backward compatibility is sacrificed. I will continue to use my custom css loader for now and will let you know if I find any bugs with my current implementation.

@neutraali
Copy link

Definitely a problem - We have to link our styles into one big lump from various sources, including some @ node_modules. However, we're running into a problem where (understandably) all the node_modules styles are trying to link to files within their own directories, resulting in a ton of errors and a failed compilation. It boggles me how with gulp-less everything just works, whereas trying to go with a pure webpack 2.0 -route with less-loader all we get are a ton of cryptic error messages .

@jhnns
Copy link
Member

jhnns commented Mar 30, 2017

Just recognized that @SplicePHP has received a lot of upvotes. I'm not sure if I understand the use-case correctly – and maybe there are different expectations here.

If you don't agree with the current behavior, you can simply overwrite that by passing the relativeUrls: false option to the less-loader. If you don't want the css-loader to resolve urls, you can disable that.

If you don't want all of that behavior, it is still legitimate to just compile Less without touching webpack at all. All that URL resolving behavior makes sense if you want to treat every file as a single module, but if you don't want that, don't use webpack for your styles. It's ok to use webpack for JS only and handle CSS with a different tool. Webpack and gulp/grunt/npm-scripts are not the same, they serve slightly different purposes.

@sekoyo
Copy link

sekoyo commented Mar 13, 2018

Relative urls has no effect when using programtically, I tried less.render(raw, { javascriptEnabled: true, relativeUrls: true }) but it still imports from root of the module

@sberney
Copy link

sberney commented Sep 7, 2018

The situation is: there is less in one directory with imports relative to that directory, and less in a second directory with imports relative to that directory. The less in the second directory imports the less in the first directory.

gulp-less handles this with no problem. When I use webpack with less-loader, this is no longer handled. I tried adding the relativeUrls: false option, as well as relativeUrls: true. Neither of those make this situation work.

I haven't got it to a state where I can test that it's totally working, but after adding { url: false } to css-loader with { relativeUrls: false } it does compile.

@Jogai
Copy link

Jogai commented Sep 16, 2019

@sberney's solution works for @lipis's flag-icon-css but then breaks for zavoloklom's material-design-iconic-font

Using url loader doesnt help either, so how should I solve this?

@cap-Bernardito
Copy link
Member

cap-Bernardito commented Apr 21, 2020

Fixed in master.

PS: To using properly flag-icon-css package, you must redefine @flag-icon-css-path variable:

@import "~flag-icon-css/less/flag-icon.less";

@flag-icon-css-path: '~flag-icon-css/flags';

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

No branches or pull requests