How to import CSS from node_modules in webpack angular2 app - css

Let's say that we start with the following starter pack:
https://github.com/angularclass/angular2-webpack-starter
After npm install and npm run start everything works fine.
I want to add an external css module, for example bootstrap 4's css (and only the css). (I know that bootstrap has a bootstrap-loader, but now I'm asking for general solution, so please think about bootstrap 4 here as it could be any other css module that is available via npm).
I install bootstrap via npm: npm install bootstrap#4.0.0-alpha.4 --save
First I thought that it is enough to add import 'bootstrap/dist/css/bootstrap.css'; to the vendor.browser.ts file.
But it isn't.
What should I do to have a proper solution?
Solutions I'm NOT asking for:
"Copy the external css module to the assets folder, and use it from there"
I'm looking for a solution that works together with npm package.
"Use bootstrap-loader for webpack"
As I described above, I'm looking for a general solution, bootstrap is only an example here.
"Use another stack"
I'm looking for a solution in the exact starter pack that I've mentioned above.

It is possible by using #import '~bootstrap/dist/css/bootstrap.css'; on the styles.css file. (Note the ~)
Edit: How it works - The '~' is an alias set on the webpack config pointing to the assets folder... simple as that..
Edit 2: Example on how to configure webpack with the '~' alias...
this should go on the webpack config file (usually webpack.config.js)...
// look for the "resolve" property and add the following...
// you might need to require the asset like '~/bootsrap/...'
resolve: {
alias: {
'~': path.resolve('./node_modules')
}
}

You won't be able to import any css to your vendors file using that stack, without making some changes.
Why? Well because this line:
import 'bootstrap/dist/css/bootstrap.css';
It's only importing your css as string, when in reality what you want is your vendor css in a style tag. If you check config/webpack.commons.js you will find this rule:
{
test: /\.css$/,
loaders: ['to-string-loader', 'css-loader']
},
This rule allows your components to import the css files, basically this:
#Component({
selector: 'app',
encapsulation: ViewEncapsulation.None,
styleUrls: [
'./app.component.css' // this why you import css as string
],
In the AppComponent there's no encapsulation, because of this line encapsulation: ViewEncapsulation.None, which means any css rules will be applied globally to your app. So you can import the bootstrap styles in your app component:
#Component({
selector: 'app',
encapsulation: ViewEncapsulation.None,
styleUrls: [
'./app.component.css',
'../../node_modules/bootstrap/dist/css/bootstrap.css'
],
But if you insist in importing to your vendor.ts then you will need to install a new loader, npm i style-loader --save-dev this will allow webpack to inject css to your page. Then you need to create a specific rule, on your webpack.common.js and change the existing one:
{ //this rule will only be used for any vendors
test: /\.css$/,
loaders: ['style-loader', 'css-loader'],
include: [/node_modules/]
},
{
test: /\.css$/,
loaders: ['to-string-loader', 'css-loader'],
exclude: [/node_modules/] //add this line so we ignore css coming from node_modules
},
The firs rule will be only applied when you try to import css, from any package inside node_modules the second rule will be applied to any css that you import from outside the node_modules

So here is a way to import various CSS files using the angular-cli which I find the most convenient.
Basically, you can refer to the CSS files (order is important if you will be overriding them) in the config and angular-cli will take care of the rest. For instance, you might want to include a couple of styles from node-modules, which can be done as follows:
"styles": [
"../node_modules/font-awesome/css/font-awesome.min.css",
"../node_modules/primeng/resources/primeng.min.css",
"styles.css"
]
A sample full-config might look like this:
.angular-cli.json
{
"$schema": "./node_modules/#angular/cli/lib/config/schema.json",
"project": {
"name": "my-angular-app"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"favicon.ico"
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"../node_modules/font-awesome/css/font-awesome.min.css",
"../node_modules/primeng/resources/primeng.min.css",
"styles.css"
],
"scripts": [],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}
],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"lint": [
{
"project": "src/tsconfig.app.json",
"exclude": "**/node_modules/**"
},
{
"project": "src/tsconfig.spec.json",
"exclude": "**/node_modules/**"
},
{
"project": "e2e/tsconfig.e2e.json",
"exclude": "**/node_modules/**"
}
],
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "scss",
"component": {}
}
}

Related

How to (successfully) bundle css for dynamic multi-page app using Webpack 5

I am trying to use Webpack 5 to bundle assets for a dynamic multi-page Django application. Using WebpackManifestPlugin and django-manifest-loader. I have this working fine for JavaScript, but I've tried every tip I can find and have not been able to make it work for css.
I have created a css file to use as an entry point and (for proof of concept) imported 1 of the application's css files into that. The output file that is produced from that is effectively empty. If I add any rules directly to the entry .css file, then those rules show up in the output file, but the #import … is gone and the rules from the imported file are not present.
Incidentally, if I purposefully mis-name the file in the import, then bundling fails, so I think the imported css is being correctly recognized and processed, then omitted. Based on some of the reading I have done, I added sideEffects: true (see django/webpack.config.js contents below) but that did not change the results.
Any advice? I've been tearing my hair out for almost 2 days on this.
django/ui/src/index.css
#import 'css/components/navigation/notifications.css';
Resulting django/dist/main.512f6e37f2c08258132d.css
/*!******************************************************************************************************!*\
!*** css ./node_modules/css-loader/dist/cjs.js!./ui/src/css/components/navigation/notifications.css ***!
\******************************************************************************************************/
/*!***********************************************************************************************************!*\
!*** css ./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./ui/src/index.css ***!
\***********************************************************************************************************/
Here's what I have in my django/webpack.config.js file:
/*global __dirname, module, require*/
const path = require('path');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const {WebpackManifestPlugin} = require('webpack-manifest-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: {
main: ['./ui/src/index.js', './ui/src/index.css'],
},
devtool: 'inline-source-map',
plugins: [
// Remove outdated assets from the output dir
new CleanWebpackPlugin(),
// Generate the required manifest.json file
new WebpackManifestPlugin(),
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
}),
],
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"sass-loader",
],
sideEffects: true,
},
{
test: require.resolve('vue'),
loader: 'expose-loader',
options: {
exposes: ['Vue'],
},
},
],
},
output: {
// Rename files from example.js to example.8f77someHash8adfa.js
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
// https://webpack.js.org/migrate/5/
// > * 404 errors pointing to URLs containing auto
// > - Not all ecosystem tooling is ready for the new default
// > automatic publicPath via output.publicPath: "auto"
// > - Use a static output.publicPath: "" instead.
publicPath: '',
},
resolve: {
alias: {
// If using the runtime only build
vue$: 'vue/dist/vue.runtime.esm.js', // 'vue/dist/vue.runtime.common.js' for webpack 1
// Or if using full build of Vue (runtime + compiler)
// vue$: 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1
},
},
};
In case it's helpful, here's what is in my django/package.json:
{
"name": "hub-ui",
"version": "0.0.1",
"description": "",
"main": "index.js",
"scripts": {
"start": "webpack --watch --mode=development",
"build": "webpack --mode=production",
"dev": "webpack --mode=development"
},
"keywords": [],
"author": "Cliosoft",
"devDependencies": {
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^6.3.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^7.18.0",
"expose-loader": "^3.0.0",
"mini-css-extract-plugin": "^2.3.0",
"node-sass": "^6.0.1",
"sass-loader": "^12.1.0",
"style-loader": "^3.2.1",
"webpack": "^5.0.0",
"webpack-cli": "^4.8.0",
"webpack-manifest-plugin": "^4.0.2"
},
"dependencies": {
"bootstrap": "^5.1.1",
"bootstrap-vue": "^2.21.2",
"vue": "^2.6.14"
},
"engines": {
"node": "~16.9",
"npm": "~7.23"
}
}
This turned out to be a "did you turn it on?" kind of problem.
The css file I was using as the test case was supposedly a copy of a css file from its previous location, but it was actually an empty file of the same name. Doh!
Once I actually copied the styles into the .css file, everything started to work correctly.
Hopefully, this will be of some help to someone in the future. If you spend many days trying to figure out what's wrong with your package.json, webpack.config.js, etc. and can't find anything wrong with them, then maybe the problem is somewhere else like not having the content in your source files that you think you have.

You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file."

I'm setting up webpack to my react project using yarn and this error appears:
ERROR in ./src/app.js 67:6 Module parse failed: Unexpected token
(67:6) You may need an appropriate loader to handle this file type,
currently no loaders are configured to process this file. See
https://webpack.js.org/concepts#loaders
ℹ 「wdm」: Failed to compile.
webpack.config.js
const path = require("path");
module.exports = {
entry: "./src/app.js",
output: {
path: path.join(__dirname, 'public'),
filename: 'bundle.js'
},
module:{
rules:[{
loader: 'babel-loader',
test: '/\.(js|jsx)$/',
exclude: /node_modules/
}]
},
devtool: 'cheap-module-eval-source-map',
devServer: {
contentBase: path.join(__dirname, 'public')
}
}
.babelrc
{
"presets": ["env", "react","#babel/preset-env"],
"plugins": [
"transform-class-properties"
]
}
package.json
{
"name": "react",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"serve": "live-server public/",
"build": "webpack",
"dev-server": "webpack-dev-server"
},
"dependencies": {
"babel-cli": "6.24.1",
"babel-core": "6.25.0",
"babel-plugin-transform-class-properties": "6.24.1",
"babel-preset-env": "1.5.2",
"babel-preset-react": "6.24.1",
"live-server": "^1.2.1",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"webpack": "^4.39.3",
"webpack-dev-server": "^3.8.0"
},
"devDependencies": {
"#babel/core": "^7.6.0",
"babel-loader": "^8.0.6",
"webpack-cli": "^3.3.8"
}
}
You are using unnecessary escape character: which is not required.
Replace test: '/\.(js|jsx)$/', with test: /\.js$|jsx/, it should work fine.
I replicated your issue in my machine and found the same which is resolved by the above fix.
hope this helps, happy coding!!!
The selected answer missing some details:
It should be test: /\.js|\.jsx$/
\: is an escape character in this case for .
|: is an Alternation / OR operand
$: is end of line
Hopefully this is useful for you.
Source:
https://www.rexegg.com/regex-quickstart.html
This error happened to me simply because the file was placed in the parent folder of the project (i.e. outside the project's folder).
Moving the file to the appropriate folder fixed it.
In my case, I configure this and it works:
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-react", "#babel/preset-env"],
},
},
},
If you get this error while working on a file, before worrying about modifying webpack config, please check if the file leading to the error has an extension i.e .js or .jsx. I encountered this error when I forgot to add an extension on one of my Javascript files. i.e. instead of my-file.js, I had my-file
I know this is not a direct answer to the original question, but this is one of the most likely places people will end up when they are having trouble with a loader.
In my case the file that it was complaining about was inside of the node_modules folder. I was consuming a typescript file directly.
{
test: /\.m?ts$|\.tsx?$/,
// exclude: /node_modules/,
use: {
loader: "ts-loader",
options: {
onlyCompileBundledFiles: true,
}
},
},
The change I made was removing the exclude portion and adding onlyCompileBundledFiles to ts-loader. Other loaders may have something similar. But basically it will only process items included in the bundle regardless of if they are inside or outside of the node_modules folder.
Put the code mentioned below in webpack file
mix.extend(
"graphql",
new (class {
dependencies() {
return ["graphql", "graphql-tag"];
}
webpackRules() {
return {
test: /\.(graphql|gql)$/,
exclude: /node_modules/,
loader: "graphql-tag/loader"
};
}
})()
);
mix.js("resources/js/app.js", "public/js").vue();
mix.graphql();
add a .babelrc file to the same folder where your node_modules folder is, with the below content
{
"presets": ["#babel/preset-react"]
}

How to use autoprefixer for external css files - webpack

I have all the css files inside ${ROOT}/resource/css/. While building using webpack, I want to copy all the css files to ${ROOT}/build/css/ with desired vendor prefixes added to all the css rules.
Currently I am using copywebpackplugin to copy all the css files as is to ${ROOT}/build/css/. I am struggling to add add a step to add vender prefixes. Please help.
Below the my webpack config:
config = {
entry: {
"new": "./resources/js/new",
"view": "./resources/js/view",
"edit": "./resources/js/edit",
"preview": "./resources/js/preview",
"sidebar": "./resources/js/sidebar",
"frame": "./resources/js/frame"
},
output: {
filename: "[name].js",
path: "build/js"
},
module: {
loaders: [
{test: /\.js$/, loaders: ["babel-loader", "eslint-loader"]}
]
},
plugins: [
new CopyWebpackPlugin([{
from: './resources/css/*.css',
to: './build/css/[name].css'
}])
]
}

How to add css library to project generated with angular-cli#webpack

After creating new project and upgrading it to webpack version I wanted to add bootstrap's CSS.
I tried method descibed in docs [1] but it doesn't seem to work.
I cannot use the cdn version because my users may have to work without acces to external networks.
[1] https://github.com/angular/angular-cli#global-library-installation
"apps": [
{
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.css"
],
...
.
$ ng --version
angular-cli: 1.0.0-beta.11-webpack.2
node: 5.4.1
os: linux x64
or maybe I just don't understand what should happen?
after ng build in dist dir there is no CSS file and there is nothing added to index.html
If you upgrade to 1.0.0-beta.11-webpack.3 or higher, you can use the apps[0].styles property of angular-cli.json to list external stylesheets for import. With this you don't have to add anything to index.html.
To upgrade from 1.0.0-beta.11-webpack.2 to 1.0.0-beta.11-webpack.3, run:
npm uninstall -g angular-cli
npm cache clean
npm install -g angular-cli#1.0.0-beta.11-webpack.3
Note: you may need to upgrade to Node.js 6 if you get SyntaxError: Unexpected token ... errors on running ng version. See https://github.com/angular/angular-cli/issues/1883 for details.
If you generate a new project and install Bootstrap, your angular-cli.json should look something like this:
{
"project": {
"version": "1.0.0-beta.11-webpack.3",
"name": "demo"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": "assets",
"index": "index.html",
"main": "main.ts",
"test": "test.ts",
"tsconfig": "tsconfig.json",
"prefix": "app",
"mobile": false,
"styles": [
"styles.css",
"../node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [],
"environments": {
"source": "environments/environment.ts",
"prod": "environments/environment.prod.ts",
"dev": "environments/environment.dev.ts"
}
}
],
"addons": [],
"packages": [],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "css",
"prefixInterfaces": false,
"lazyRoutePrefix": "+"
}
}
I think that you have to add ../ front of the node_modules, because node_modules folder is one step up in the directory tree.
Like this:
"../node_modules/bootstrap/dist/css/bootstrap.css"
All your css files from apps[0].styles property of angular-cli.json during ng build are compiled into styles.bundle.js and this file is included in index.html. If you check this file you can found there all styles. So it works as intended.

How do I add global styles without requiring an extra component?

I want to apply some global styles to my website (body, h1, h2, h3, etc).
To do this with Angular2, the view encapsultation of a component needs to be set thusly: encapsulation: ViewEncapsulation.None.
example:
#Component({
selector: 'app-root',
templateUrl: template(),
styleUrls: ['global.scss', 'app.component.scss'],
encapsulation: ViewEncapsulation.None,
})
export class AppComponent {
title = 'Hello world!';
}
The problem is that this encapsulation rule applies to all of this components stylesheets, which means I must have a separate component just for global styles.
Is there another way to do this without requiring an extra component and without needing to edit Angular-CLI's build config?
(I'm using angular/core 2.0.0-rc.5 and angular-cli 1.0.0-beta.11-webpack.2)
The PR mentioned by drbishop has been merged and released as 1.0.0-beta.11-webpack.3.
To upgrade from 1.0.0-beta.11-webpack.2 to 1.0.0-beta.11-webpack.3, run:
npm uninstall -g angular-cli
npm cache clean
npm install -g angular-cli#1.0.0-beta.11-webpack.3
Note: if you get SyntaxError: Unexpected token ... errors on running ng version after upgrading you may need to upgrade to Node.js 6. See https://github.com/angular/angular-cli/issues/1883 for details.
If you generate a new project using 1.0.0-beta.11-webpack.3, you can add a styles.css file to your src directory which will be automatically included in your build. You can also add external CSS imports to the apps[0].styles property of angular-cli.json.
Your angular-cli.json should look something like this for a new project generated by 1.0.0-beta.11-webpack.3:
{
"project": {
"version": "1.0.0-beta.11-webpack.3",
"name": "demo"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": "assets",
"index": "index.html",
"main": "main.ts",
"test": "test.ts",
"tsconfig": "tsconfig.json",
"prefix": "app",
"mobile": false,
"styles": [
"styles.css"
],
"scripts": [],
"environments": {
"source": "environments/environment.ts",
"prod": "environments/environment.prod.ts",
"dev": "environments/environment.dev.ts"
}
}
],
"addons": [],
"packages": [],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "css",
"prefixInterfaces": false,
"lazyRoutePrefix": "+"
}
}
This is currently being designed and will be implemented before a final release. The general idea will be to provide a reference to a style file (CSS/SCSS/LESS...) and have it included within the application.
As mentioned before, it's being implemented for future releases. There's already a pull request to fix this. You can update it manually as a workaround for now.
Then, update your angular-cli.json file:
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": "assets",
"index": "index.html",
"main": "main.ts",
"test": "test.ts",
"tsconfig": "tsconfig.json",
"mobile": false,
"additionalEntries": [
{ "input": "polyfills.ts", "output": "polyfills.js" },
"styles.sass"
],
"environments": {
"source": "environments/environment.ts",
"prod": "environments/environment.prod.ts",
"dev": "environments/environment.dev.ts"
}
}
],

Resources