I am facing a problem in Webpack regarding Relative Path.
Let me try to explain the scenario :
I have 2 separate project in Workspace directory :
Project-A [Bundling using Gulp] : Stable & Working
Project-B [Bundling using Webpack] : New project
As both the projects are using same Styling, so I wanted to reuse the SCSS files [consisting of standard variables, predefined layouts, modals, classes etc] of Project A into Project B.
Now, if I am trying to import Project-A index.scss in Project-B index.scss as another partial [Commenting out the Background Image URL Depency], webpack is able to generate the required CSS output file.
// Import Project A SCSS [Common Varibles, Classes, Styling etc]
#import "../../../../Project_A/assets/stylesheets/index";
But as Project-A's index.scss is further referring background images from the respective Relative-Path, the webpack build is throwing error
'File / dir not found in XYZ/Project-B/Source/Stylesheets'.
Exact Error Block :
ERROR in ./src/assets/stylesheets/index.scss
Module build failed: ModuleNotFoundError: Module not found: Error: Cannot resolve 'file' or 'diWorkSpace\Project_B\src\assets\stylesheets
screenshot :
I am not able to understand, why Webpack is not able to resolve the Relative path of assets inside Project-A and still looking inside 'Project B'.
Here is the Code-Repo URL for the simulated issue :
https://github.com/raviroshan/webpack-build-issue/tree/master/WorkSpace
Steps to reproduce.
Download the Repo.
Browse inside Project_B folder, and do a NPM install.
Run 'webpack'. It would build correctly as Relative Image
URL code is commented out.
Now put back the commented line of code : https://github.com/raviroshan/webpack-build-issue/blob/master/WorkSpace/Project_A/assets/stylesheets/index.scss#L27
So, finally after so much struggle, got a proper SOLUTION.
It turns out to be an issue with CSS-loader i.e it is not able to resolve the URL with respective to current file.
Using resolve-url-loader solved this problem.
https://www.npmjs.com/package/resolve-url-loader
// Old Loader Config in Webpack-entry
loader: ExtractTextPlugin.extract('style-loader', 'css-loader?sourceMap!sass-loader?sourceMap')
// New [Fixed] Loader Config in Webpack-entry
loader: ExtractTextPlugin.extract('style-loader', 'css-loader?sourceMap!resolve-url-loader!sass-loader?sourceMap')
Here is updated Code-Repo with solution : https://github.com/raviroshan/webpack-build-issue
Note : Don't omit -loader
Your Webpack.config.js should always use the long-form of the loader name (i.e. the -loader suffix).
There is another package called resolve-url which Webpack can confuse with resolve-url-loader.
It seems like it's css-loader fault and the way it resolves paths in #import and url(). It tries to resolve all paths — even those from imported stylesheets — relative to the main CSS file — which in your case is /Project_B/src/assets/stylesheets/index.scss.
Don't cry! There's a solution!
Maybe it's not perfect, but it's the best one I came with so far.
Create a global variable $assetsPath holding a path to assets relative to the current stylesheet. Then prepend this variable to all your url() values.
In your /Project_A/assets/stylesheets/index.scss you'd write:
/*/ Using !default we can override this variable even before the following line: /*/
$assetsPath: '../' !default;
.container {
/*/ ... /*/
.content-wrapper {
/*/ ... /*/
background-image: url($assetsPath + "images/content-bg.jpg");
}
}
In your /Project_B/src/assets/stylesheets/index.scss you'd write:
/*/ The following variable will override $assetsPath defined in the imported file: /*/
$assetsPath: '../../../../Project_A/assets/';
/*/ Import Project A SCSS [Common Varibles, Classes, Styling etc] /*/
#import "../../../../Project_A/assets/stylesheets/index";
The Aftermath
If you bundle Project-A with Gulp it's gonna see Project-A's code as:
/*/ ... /*/
background-image: url("../images/content-bg.jpg");
Although, when you bundle Project-B with Webpack it's gonna see Project-A's code as:
/*/ ... /*/
background-image: url("../../../../Project_A/assets/images/content-bg.jpg");
Therefore, you are saved.
Most definitely I'll look closer at this issue. All of this could be avoided if url-loader would respect a path in the #import statement and apply it to referenced assets accordingly. Either I'm missing something or it should be considered as a bug.
I hope you have a wonderful day!
~Wiktor
you need to set the publicPath as a relative path to get the relative path in file-loader.
Related
Hi everyone :) I was hoping I could get some fresh eyes on a problem I'm running into on my project:
I have a NextJS (12.1.4) project using absolute imports, I also have an NPM package where I directly import the source files in my project (since it's a library for shared components, internal use only). This package is called bs-components here.
Here is my jsconfig.json file
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"#bscom/*": ["node_modules/#company/bs-components/*"],
"*": ["node_modules/#company/bs-components/*", "*"]
}
}
}
I will explain the intent behind my paths config here:
The first entry for #bscom/* will allow me to import modules from my library using a shorthand.
The second entry for * is intended so that the module resolution goes like this:
Look in the component library first for a module, so that absolute imports in the component library are respected.
If module is not found in the library, then resolve using the baseUrl as the starting point for resolution.
Unfortunately when using this config, absolute imports within the component library fail with the following error:
./node_modules/#company/bs-components/styling/resets.js:1:0
Module not found: Can't resolve 'constants/colors'
This happens in my NextJS _document.js file, so during the server render step.
There are no modules with that path in my main project which could cause a conflict.
Any suggestions would be greatly appreciated, thanks :)
I am building a SPA with Vue (Quasar actually) in which I need to be able to:
Load the contents of a CSS file into a JS variable (or Vue data
property) at runtime
This CSS file should be produced at build time
from a SCSS file
I don’t want to load a pre-made CSS file, I want to be able to author the CSS code via SASS.
I also don’t want to compile the CSS from SCSS at runtime, e.g. on every app load.
In other words I have the following workflow in mind:
Author the CSS in a pre-defined SCSS file that is part of my project structure
At build time (or at run-dev time) I want that this SCSS is compiled into a CSS file
Then at runtime, in one of my Vue components, I want to load the previously produced CSS code as string into a variable
The reasoning for that is that this CSS code will then be fed into and iframe (via postMessage-ing) and the iframe will use CSSStyleSheet’s insertRule() to apply the styles to the page.
How should I configure my project and packages so that this can happen? One thing that I found already is that I might need to use the raw-loader but how do I prepare the CSS file when building the project so that the raw-loader can get it at runtime?
From an initial look you have two problems here both of which are relatively simple.
The first one is you need to include a scss compiler plugin during your projects build step. Which one you use will depend on any existing tooling you may be using. Otherwise, you can just drop in https://www.npmjs.com/package/node-sass
The second issue is how to acquire the css at runtime. You have a couple options here. You can compile the file into your bundle or you could retrieve it at runtime.
Runtime would keep your bundle small and allow you to serve the css normally but this requires a server to serve it. Compile time would be faster to load initially but increase your bundle size.
If you are using webpack you could use the raw loader you linked but if you are not currently using webpack that is probably out of scope.
You could do this by adding a new step to your build that converts the css into a String literal which would alleviate the need to load it at runtime but increase your bundle side.
For loading at runtime, you could easily retrieve the file via ajax from an http server.
I have found the following solution:
Install the "raw-loader" loader
Import the SCSS using the following statement:
import style from '!raw-loader!sass-loader!./my-styles.scss'
Note: the "!" at the beginning is to override the default loaders configuration. In my case Quasar chains the "style-loader" for SCSS files by default (to output a tag in the head) so I have to disable it in this case.
And now I have the compiled CSS code in the style variable.
First, run the following.
npm install path sass
Aftre that...
const path = require("path");
const sass = require("sass");
const css = sass.compile(path.join(__dirname, "style.scss")).css;
console.log(css);
I decided to try out WebPack on a new project I'm spinning up today and I'm getting really strange behavior from the sourcemaps. I can't find anything about it in the documentation, nor can I find anyone else having this issue when skimming StackOverflow.
I'm currently looking at the HelloWorld app produced by Vue-CLI's WebPack template -- no changes have been made to the code, the build environment, or anything.
I installed everything and ran it like so:
vue init webpack test && cd test && npm install && npm run dev
Looking at my sourcemaps, I see the following:
This is a hot mess. Why are there three version of HelloWorld.vue and App.vue? Worse yet, each version has a slightly different version of the code and none of them match the original source. The HellowWorld.vue sitting in the root directory does match the original source, but what's it doing down there instead of in the ./src/components folder? Finally, why isn't there a fourth App.vue that has the original source for it?
As far as I can tell this may have something to do with the WebPack loaders. I've never gotten these kinds of issues with any other bundler, though. Below is an example of the exact same steps using the Browserify Vue-CLI template:
No webpack:// schema, only one copy of every file, the files actually contain the original source code (kind of important for source maps), no unexpected (webpack)/buildin or (webpack)-hot-middleware, no . subdirectory,.... just the source code.
I haven't worked with Vue so can't really describe how exactly this is happening but it seems to be related to Vue Loader. Looking at the documentation I did not really find anything that clarifies why it would create three different files for one component. But it does seem logical considering that a .vue file might contain three types of top-level language blocks: <template>, <script>, and <style>.
Also, looking at two of those files you do see a comment at end of each file that suggests it was modified in some way by a Vue loader. Either this
//////////////////
// WEBPACK FOOTER
// ./node_modules/vue-loader/lib/template-compiler
or
//////////////////
// WEBPACK FOOTER
// ./node_modules/vue-style-loader!./node_modules/css-loader
The third file is different but it still does have code that identifies it as being modified by Vue loader. Here is some of that code
function injectStyle (ssrContext) {
if (disposed) return
require("!!vue-style-loader...")
}
/* script */
import __vue_script__ from "!!babel-loader!../../node_modules/vue-loader/..."
/* template */
import __vue_template__ from "!!../../node_modules/vue-loader/..."
/* styles */
var __vue_styles__ = injectStyle
The document also says this:
vue-loader is a loader for Webpack that can transform Vue components written in the following format into a plain JavaScript module:
Which explains why you might not see the same type of behaviour with other bundlers.
Now, This might not be the answer you were looking for but just wanted to share what I had found.
This is actually a feature of webpack.
webpack has HMR (Hot Module Reloading). If you look in your network tab, go ahead and make an update to your HelloWorld.vue file. You'll see a js chunk come thru as well as an updated JSON manifest. Both of these will have a unique hash at the end for each time you make a change to the application. It does this so the browser does not have to do a full reload.
For a better explanation of this I would highly recommend reading through https://webpack.js.org/concepts/hot-module-replacement/
I am converting code from javascript to typescript as a part of a migration process.
I need to import 'xyz.css' file in my another file like 'abc.ts'. both the files are in the same directory. I am importing the file as follows :
import '../../xyz.css';
and it gives me an error :
relative modules were not found:
'../../../xyz.css' in '../../dist/abc.js'
This error occurs while webpack compilation process.
Please give a suggestion to resolve same.
Thanks!
For non-node_modules based modules, you should use a path, usually a relative path. So if you're trying to reference xyz.css from the same directory as your typescript file, you would use
import './xyz.css';
I've downloaded an angular starter and I'm trying to install Bootstrap on it. They explain how to do that in the External Stylesheet section.
I've installed bootstrap:
npm i bootstrap --save
npm i bootstrap-sass --save
and then added in the top of styles.scss:
#import 'bootstrap/scss/bootstrap.scss';
But it's marked with red mentions:
Cannot resolve directory 'bootstrap'
Any help will be profoundly appreciated!
As previously mentioned, you need to add the tilde when you're importing inside your css.
#import 'bootstrap/scss/bootstrap.scss';
Second, the bootstrap css file is loading fonts along with the stylesheet, resulting in the error you're having because it's using relatives paths webpack doesn't know where to pickup.
To fix that, you could either change the path of the folder to the correct one like the following as seen from here:
$icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap";
#import "~bootstrap-sass/assets/stylesheets/bootstrap";
And add the fonts loader to your webpack configuration:
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
loader: 'url-loader',
}
But the easiest way for you to get around the problem would be to remove the style import you are doing, remove the bootstrap-sass dependency and use the cdn instead.
#import url('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css');
The advantage of doing this is that clients that have already downloaded bootstrap using this cdn will get the cached version. Since a lot of websites theses days use it, It's very likely they won't have to download it again, and it also saves you the trouble of hosting it or incorporating into your bundle.
Nothing exists at src/styles/bootstrap/scss/bootstrap.scss, so you can either manually copy/paste (or download) a bootstrap.scss file there OR just import bootstrap into your src/styles/styles.scss from your node_modules via:
#import "~bootstrap-sass/assets/stylesheets/bootstrap";
You'll get font-related errors until you add in a line before that so your styles.scss reads:
$icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/";
#import "~bootstrap-sass/assets/stylesheets/bootstrap";
More: https://stackoverflow.com/a/35575175/3814251
In order to let sass-loader import a sass module from mode_modules folder, prepend the module path with a ~. Otherwise the provided path is resolved like a relative one:
#import "~bootstrap/scss/bootstrap.scss";
For more info check out sass-loader docs.