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

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.

Related

How to set dev and prod targets in Parcel

Please can anyone shed some light on how "Targets" work in Parcel, or at least how to separate unminified dev and minified prod files. The sparse documentation just raises further questions and no amount of Googling will find a solution. I am new to Parcel and to the modular nature of newer Javascript syntax.
This all started when I noticed that parcel watch does not minify/uglify the JS, and I don't want that to end up on the prod server. But parcel build WILL minify/uglify the JS. So it would be really swell if my start script could bundle files into build/wp-content/themes/yourproject/assets--dev and the build script could bundle files into build/wp-content/themes/yourproject/assets. That way I can put assets-dev in .gitignore and use those while building the site. Or is that even the proper way to acheive this? In Gulp you can just define different tasks or create one task to do both of these outputs, but I cannot find a way to do this in Parcel.
Description of the project - It's a WordPress site so I can't necessarily feed Parcel source html files to scan. Parcel is really just bundling JS and SCSS files, we are replacing our old Gulp setup. So in package.json, I have this (I'll explain what I have tried in "scripts" and in "targets" further down):
{
"name": "localtesting2",
"version": "1.0.0",
"description": "## Overview",
"browserslist": "> 0.5%, last 2 versions, not dead",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "parcel watch develop/js/index.js develop/styles/style--critical.scss develop/styles/style--noncritical.scss --dist-dir ./build/wp-content/themes/yourproject/assets--dev",
"build": "parcel build develop/js/index.js develop/styles/style--critical.scss develop/styles/style--noncritical.scss --dist-dir ./build/wp-content/themes/yourproject/assets"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"smoothscroll-polyfill": "^0.4.4"
},
"devDependencies": {
"#parcel/transformer-sass": "^2.3.2",
"autoprefixer": "^10.4.2",
"parcel": "latest",
"parcel-bundler": "^1.12.5",
"parcel-resolver-ignore": "^2.0.0",
"postcss": "^8.4.6",
"postcss-modules": "^4.3.0",
"sass": "^1.49.8",
"webfontloader": "^1.6.28"
},
"targets": {
"default": {
"distDir": "build/wp-content/themes/yourproject/assets--dev"
},
"build": {
"distDir": "build/wp-content/themes/yourproject/assets"
}
},
"parcelIgnore": [
"images/*.*",
"webfonts/*.*"
]
}
The root of the project is like this:
build/
-- build/wp-admin
-- build/wp-content
-- etc all the WordPress core/theme files that will ultimately end up on the server
The dev directory looks like this, basically just holding the source code for JS and CSS that get compiled into the theme:
In "scripts" I have tried setting "start" and "build" both with and without the --dist-dir flag. And I remove the "targets" object from package.json when I try --dist-dir.
For "targets", I can't find clear documentation/examples on what exactly goes here so I just tried anything I could think of. I have tried all of the following but nothing works the way I am intending it to. Parcel picks either assets-dev or assets and compiles everything there, it won't separate the code. Sometimes it will just compile files into a dist folder, which I am not using.
"targets": {
"start": {
"distDir": "build/wp-content/themes/yourproject/assets--dev"
},
"build": {
"distDir": "build/wp-content/themes/yourproject/assets"
}
},
"targets": {
"watch": {
"distDir": "build/wp-content/themes/yourproject/assets--dev"
},
"build": {
"distDir": "build/wp-content/themes/yourproject/assets"
}
},
"targets": {
"default": {
"distDir": "build/wp-content/themes/yourproject/assets--dev"
},
"build": {
"distDir": "build/wp-content/themes/yourproject/assets"
}
},
"targets": {
"dev": {
"distDir": "build/wp-content/themes/yourproject/assets--dev"
},
"prod": {
"distDir": "build/wp-content/themes/yourproject/assets"
}
},
Ultimately I am looking for how to get unminified assets into one folder that I can conditionally load, and minified assets into another folder that will get loaded on the prod server.
I figured it out. The CLI commands in the "scripts" portion of package.json can include the --target flag. This way you can name a target and then define it in the "targets" property. This is the working package.json:
{
"name": "localtesting2",
"version": "1.0.0",
"description": "## Overview",
"browserslist": "> 0.5%, last 2 versions, not dead",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "parcel watch develop/js/index.js develop/styles/style--critical.scss develop/styles/style--noncritical.scss develop/styles/editor.scss --target default",
"build": "parcel build develop/js/index.js develop/styles/style--critical.scss develop/styles/style--noncritical.scss develop/styles/editor.scss --target build"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"smoothscroll-polyfill": "^0.4.4"
},
"devDependencies": {
"#parcel/transformer-sass": "^2.3.2",
"autoprefixer": "^10.4.2",
"parcel": "latest",
"parcel-bundler": "^1.12.5",
"parcel-resolver-ignore": "^2.0.0",
"postcss": "^8.4.6",
"postcss-modules": "^4.3.0",
"sass": "^1.49.8",
"webfontloader": "^1.6.28"
},
"targets": {
"default": {
"distDir": "build/wp-content/themes/yourproject/assets--dev"
},
"build": {
"distDir": "build/wp-content/themes/yourproject/assets"
}
},
"parcelIgnore": [
"images/*.*",
"webfonts/*.*"
]
}
Still though there must be a better workflow for this, or maybe a way to get the watch command to minify everything like build does.

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.

Cannot find module 'autoprefixer' when running npx tailwindcss init -p command

I'm using Vue 3 and trying to add tailwindcss into it from the following tutorial. https://tailwindcss.com/docs/guides/vue-3-vite#install-tailwind-via-npm
I have installed the dependencies using the following command,
npm install -D tailwindcss#npm:#tailwindcss/postcss7-compat postcss#^7 autoprefixer#^9
But when I tried to create the configuration files using the following command
npx tailwindcss init -p
It is giving me the following error.
npx: installed 83 in 5.2s Cannot find module 'autoprefixer' Require
stack:
/~/.npm/_npx/33283/lib/node_modules/tailwindcss/lib/cli/commands/build.js
/~/.npm/_npx/33283/lib/node_modules/tailwindcss/lib/cli/commands/index.js
/~/.npm/_npx/33283/lib/node_modules/tailwindcss/lib/cli/main.js
/~/.npm/_npx/33283/lib/node_modules/tailwindcss/lib/cli.js
I don't know why autoprefixer is not detecting because I have already installed it.
Even the package.json have it.
{
"name": "wooclime",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.6.5",
"vue": "^3.0.0"
},
"devDependencies": {
"#vue/cli-plugin-babel": "~4.5.0",
"#vue/cli-plugin-eslint": "~4.5.0",
"#vue/cli-service": "~4.5.0",
"#vue/compiler-sfc": "^3.0.0",
"autoprefixer": "^9.8.6",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^7.0.0-0",
"postcss": "^7.0.35",
"tailwindcss": "npm:#tailwindcss/postcss7-compat#^2.0.2"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
Run:
npx tailwindcss-cli#latest init -p
Please uninstall run this command:
npm uninstall tailwindcss postcss autoprefixer
After this module is uninstall, please run this command:
npm install tailwindcss#latest postcss#latest autoprefixer#latest
I had the same problem when I ran the command with node version 14.15.0.
Apparently using node 15.5 solved it for me. I think there's a problem with npm or something.
You could use the following command using the tailwindcss cli with latest version an the flag --postcss or -p
npx tailwindcss-cli#latest init --postcss
Or follow these steps :
After installing that dependencies try to create the following files project root without running that command :
tailwind.config.js
module.exports = {
purge: [],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};
postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}
Then in your main CSS file add :
#tailwind base;
#tailwind components;
#tailwind utilities;
I had same issue
I downgrade the tailwindcss package to 1.0.5 from latest
Now it is working

How to import CSS from node_modules in webpack angular2 app

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": {}
}
}

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