Trying to set up gulp and one of the steps is frustrating. I am following a tutorial and can't get it to work right.
https://css-tricks.com/gulp-for-beginners/
Basically, I want to create a build task that compiles sass, concats the css files, minimizes it, and output it to the public folder. Here is my code for my index.html. (Simplified).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!--build:css css/styles.min.css -->
<link rel="stylesheet" href="scss/styles.scss">
<!-- endbuild -->
</head>
<body>
</body>
</html>
Now here is my Gulpfile.js
var gulp = require('gulp'),
sass = require('gulp-sass'),
useref = require('gulp-useref'), // Use to concatenate files
gulpIf = require('gulp-if'),
cssnano = require('gulp-cssnano'),
uglify = require('gulp-uglify'),
imagemin = require('gulp-imagemin'),
imagecache = require('gulp-cache'),
del = require('del'),
runsequence = require('run-sequence');
/* ********************************* */
// PRODUCTION TASKS ONLY \\
/*Used to start with a clean slate on the public folder */
gulp.task('clean:public', function () {
return del.sync('public');
})
gulp.task('watch:prod', function () {
gulp.watch('src/scss/**/*.scss', ['sass']);
});
gulp.task('sass:prod', function () {
return gulp.src('src/scss/**/*.scss')
.pipe(sass())
.pipe(gulp.dest('public/css'))
});
gulp.task('useref:prod', function () {
return gulp.src('src/*.html')
.pipe(useref())
.pipe(gulpIf('*.js', uglify()))
.pipe(gulpIf('*.css', cssnano()))
.pipe(gulp.dest('public'));
});
gulp.task('images:prod', function () {
return gulp.src('src/images/**/*.+(png|jpg|gif|svg)')
.pipe(imagecache(imagemin([
imagemin.gifsicle({interlaced: true}),
imagemin.jpegtran({progressive: true}),
imagemin.optipng({optimizationLevel: 5}),
imagemin.svgo({plugins: [{removeViewBox: true}]})
])))
.pipe(gulp.dest('public/images'));
});
gulp.task('cssimages:prod', function () {
return gulp.src('src/css/cssimages/**/*.+(png|jpg|gif|svg)')
.pipe(imagecache(imagemin([
imagemin.gifsicle({interlaced: true}),
imagemin.jpegtran({progressive: true}),
imagemin.optipng({optimizationLevel: 5}),
imagemin.svgo({plugins: [{removeViewBox: true}]})
])))
.pipe(gulp.dest('public/css/cssimages'));
});
/* BRING EVERYTHING TOGETHER */
gulp.task('build:prod', function (callback){
runsequence
(
'clean:public',
['sass:prod','useref:prod','images:prod', 'cssimages:prod'],
callback
)
})
As per the tutorial, this should create a file in the public folder under css names styles.min.css
This file should also already be compiled down from sass. I did an example styles.scss and inside it I have.
$bgcolor : yellow;
body {
background: $bgcolor;
}
div {
width: 100px;
height: 20px;
}
When I run gulp build:prod , this is what it outputs in styles.min.css
$bgcolor:#ff0;body{background:$bgcolor}div{width:100px;height:20px}
The files minimizing fine but i can't get the sass part run right and compile when use the build task.
^^^ As you see, instead of sassing the file and then concatenating the file, it create 2 files. I'm trying to have gulp sass the file first, and then have useref move the file to the public folder and rename it to styles.min.css
It seems I'm missing something somewhere or not sourcing/destinating to the right folders?
If I run gulp sass:prod, it works fine. But can't seem to get my build task to run right I'm stumped.
From the article that you have mentioned,
Gulp-useref concatenates any number of CSS and JavaScript files into a
single file by looking for a comment that starts with "". Its syntax is:
<!-- build:<type> <path> --> ... HTML Markup, list of script / link tags. <!-- endbuild -->
path here refers to the target path of the generated file.
According to the document you have specified the following.
<!--build:css css/styles.min.css -->
<link rel="stylesheet" href="scss/styles.scss">
<!-- endbuild -->
So the useref will copy the styles from styles.scss and creates styles.min.css and pastes the scss styles. That is the reason you are getting scss styles in the minified styles.min.css
To achieve what you wanted you have to modify your sass:prod dest path like below.
gulp.task('sass:prod', function () {
return gulp.src('src/scss/**/*.scss')
.pipe(sass())
.pipe(gulp.dest('src/css'))
});
and in the html, you have to reference the css file.
<!--build:css css/styles.min.css -->
<link rel="stylesheet" href="css/styles.css">
<!-- endbuild -->
And also as specified by #Mark, it is better to modify the run-sequence to make sure that the sass:prod task completes before the useref:prod task.
gulp.task('build:prod', function (callback){
runsequence
(
'clean:public','sass:prod',
['useref:prod','images:prod', 'cssimages:prod'],
callback
)
})
From the run-sequence documentatin :
You can still run some of the tasks in parallel, by providing an array of task names for one or more of the arguments.
So, in your tasks array :
['sass:prod','useref:prod','images:prod', 'cssimages:prod'],
these tasks run in parallel. There is no guarantee that the 'sass:prod' task will complete before the 'useref:prod' task. If you want that to happen change to:
gulp.task('build:prod', function (callback){
runsequence
(
'clean:public',
'sass:prod',
['useref:prod','images:prod', 'cssimages:prod'],
callback
)
})
Related
I'm trying to generating a pdf file which is generated from express-handlebars rendering. However, some css files don't seem to be working.
Bootstrap are still working okays, but the custom css (i'm using a theme) is not working. I have tried phantomjs config (--web-security=false,...), switching css folder directory from local to the server. But none of them seems to be working. Images are working fine.
generating html and creating pdf files
var config = {
format: "A4",
orientation: "landscape",
base: "http://127.0.0.1:3002/uploads/theme/",
timeout: 100000,
phantomArgs: ["--web-security=false","--local-to-remote-url-access=true"]
}
var html = await hbs.render('./views/pdf.handlebars', data)
await fs.writeFile("pdf.html", html, function(err) {
if(err) {
return console.log(err);
}
})
var fileName = uuid.v4()
await pdf.create(html, config).toFile(`./downloads/${fileName}.pdf`, function (err, res) {
if (err) return console.log(err);
response.send({ success: true, data: { downloadURL: fileName } })
including css files:
<link rel="stylesheet" type="text/css" href="css/style.css">
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="css/animate.min.css">
server receiving calls from css files:
Imgur
Expecting results:
Imgur
Actual result:
Imgur
As you can see, bootstrap and font-awesome are working fine, but the "style.css" is not working. Anyone have any idea about this problems? Many thanks in advance!
Your server code is reading only HTML files and not css files , so css is actually being not used, use inline css.
I am also looking for methods to write separate HTML and CSS and merge to form pdf.
I'm preparing a starter for react from scratch, here is the code: https://github.com/antondc/react-starter
I managed to set up bundling for client and server, with css modules and less, and now I'm with server side rendering. I'm doing that with a js template:
// src/server/views/index.ejs
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>INDEX.EJS</title>
<link rel="stylesheet" type="text/css" href="assets/index.css">
</head>
<body>
<div id="app"></div>
<script src="/assets/bundle.js"></script>
</body>
</html>
As you see, the link to the css file is harcoded there. But in my Webpack configuration I have this file name hashed, because I want to prevent caching from browsers when I update the code on development.
I am wondering how can I link the css file there. Now in the template I have href="assets/index.css, but the css file is in /dist/assets/d47e.css.
It would be great if would be possible to do something like href="assets/*.css, but is not possible, so what is the common approach for a problem like this one?
Thanks!
It depends.
Step 1: Get the current asset name
To get the current name of the generated webpack css/js files, you can use the assets-webpack-plugin. This will (with default config) generate an assets.json file in your output folder with essentially this structure:
{
"bundle_name": {
"asset_kind": "/public/path/to/asset"
}
}
Step 2a: Your html is rendered from a template (pug/jade/what ever)
// in your render code
const assets = require('<webpack-output-folder>/assets.json');
// ...
res.render('template', {
scripts: [{src: `${WEBPACK_PUBLIC_PATH}/${assets.myEntryPointName.js}` }],
links: [{href: `${WEBPACK_PUBLIC_PATH}/${assets.myEntryPointName.css}` rel: 'stylesheet' }],
});
// in your template (example for pug)
// ...
each link in links
link(rel=link.rel href=link.href)
// ...
each script in scripts
script(src=script.src)
// ...
Step 2b: Your html is static
You need to update the html (using a script) with the information from the asset.json file. This script needs to be run after webpack. Something like
const assets = require('<webpack-output-folder>/assets.json');
const fs = require('fs');
const css = /assets\/[a-z0-9]*\.css/i;
const js = /assets\/[a-z0-9]*\.js/i;
fs.readFile('<yourhtml>.html', (err, data) => {
// ... (error handling)
const updatedCss = data.replace(css, assets.myEntryPointName.css);
const updatedJs = updatedCss.replace(js, assets.myEntryPointName.js);
fs.writeFile('<yourhtml>.html', updated, (err) => {
// ... (error handling)
});
});
You can use HTMLWebpackPlugin to generate an HTML file that will have your JS and CSS output inserted.
my dir looks like that :
|-project
-gulpfile.js
|-build
-index.html
|-js
-app.min.js
-vendors.min.js
|-styles
-all.css
-vendors.min.css
i inject the css and js files with this gulp task:
gulp.task('index',function () {
return gulp.src('src/index.html')
.pipe(inject(gulp.src(['**/vendors.min.css','**/vendors.min.js','**/app.min.js','**/all.css'], {read: false})))
.pipe(gulp.dest('build'))
.pipe(livereload());
})
i set up a local server with node.js,when i do the request , the html file loads up,but the .js and .css files dont connect for some reason.Although when i check page's source code at output their paths are written in.
<!-- inject:css -->
<link rel="stylesheet" href="/build/styles/vendors.min.css">
<link rel="stylesheet" href="/build/styles/all.css">
<!-- endinject -->
when i hover on one of them it shows :
http://localhost:5000/build/styles/all.css
i use this task for setting the server :
gulp.task('connect', function() {
connect.server({
root: 'build',
livereload: true,
port: 5000
});
});
EDIT
any recommendation about how to make it on hovering look like
localhost:5000/styles/all.css
If the build folder is the root of the server, you shouldn't be including it in the path of the js and css files. This is failing because you are trying to reference a build folder inside the build folder.
Change your injection of css to the following:
<link rel="stylesheet" href="styles/vendors.min.css">
<link rel="stylesheet" href="styles/all.css">
I'm currently using Polymer as my front end development framework. I love SASS.
Now I understand I can create a Sass file and import it like I normally would.
However, I've really gotten into the habit of using style tags within my web components.
Basically the workflow I am looking for is to be able to simply define a script tag within my Web Component maybe add type='sass; to it. Then have grunt go through and compile all of my SASS within those tags before outputting the files to my .tmp directory.
Is something like this achievable with something like Grunt or Gulp? If so what are the best modules to help me achieve this?
My implementation is based on a replacement of a tag inside the Polymer html file. I'm using gulp but could be changed to use simply fs.
The files structure should be as this example:
app-view
|- app-view.html
|- app-view.scss
app-view.html:
<dom-module id="app-view">
<template>
<style>
<!-- inject{scss} -->
</style>
</template>
</dom-module>
app-view.scss:
:host{
margin-top: 50px;
justify-content: center;
display: flex;
}
#container{
font-size: 12px;
h1{
font-size: 20px;
}
}
gulpfile.js:
var gulp = require('gulp');
var nodeSass = require('node-sass');
var path = require('path');
var fs = require('fs');
var map = require('map-stream');
var srcPath = 'src/';
var buildPath = 'build/';
var buildSrcPath = path.join(buildPath, 'target');
gulp.task('processComponents', function () {
return gulp.src([srcPath + '/components/**/*.html'])
.pipe(map(function (file, cb) {
var injectString = '<!-- inject{scss} -->';
// convert file buffer into a string
var contents = file.contents.toString();
if (contents.indexOf(injectString) >= 0) {
//Getting scss
var scssFile = file.path.replace(/\.html$/i, '.scss');
fs.readFile(scssFile, function (err, data) {
if (!err && data) {
nodeSass.render({
data: data.toString(),
includePaths: [path.join(srcPath, 'style/')],
outputStyle: 'compressed'
}, function (err, compiledScss) {
if (!err && compiledScss) {
file.contents = new Buffer(contents.replace(injectString, compiledScss.css.toString()), 'binary');
}
return cb(null, file);
});
}
return cb(null, file);
});
} else {
// continue
return cb(null, file);
}
}))
.pipe(gulp.dest(path.join(buildSrcPath, 'components')));
});
RESULT:
<dom-module id="app-view">
<template>
<style>
:host{margin-top:50px;justify-content:center;display:flex}#container{font-size:12px}#container h1{font-size:20px}
</style>
</template>
</dom-module>
First of all, a million Thanks and gratitude goes to David Vega for showing how it is done! I made some adaptations and optimized the code a little bit.
Here's the github for the file!
https://github.com/superjose/polymer-sass/tree/master
Well, this took me a while. Here it goes!
Polymer unleashed version 1.1. From its website:
Note: Style modules were introduced in Polymer 1.1; they replace the
experimental support for external stylesheets.
Instead, they now support "shared styles".
So this means that we can import .html files with css content. The problem is that we can't do .sass the normal way.
Fortunately here's a simpler solution.
What the following script does is that it gets your .scss files, parse them, and inject them into the shared style .html.
Here is the code. Below it, it's step by step on how to use and setup:
var gulp = require('gulp');
var nodeSass = require('node-sass');
var path = require('path');
var fs = require('fs');
var map = require('map-stream');
var basePath = "app/";
var excludeDir = basePath+"bower_components/";
var ext = "**/*.html";
/**
* We need to specify to nodeSass the include paths for Sass' #import
* command. These are all the paths that it will look for it.
*
* Failing to specify this, will NOT Compile your scss and inject it to
* your .html file.
*
*/
var includePaths = ['app/elements/**/'];
gulp.task('watchSass', function(){
gulp.watch(['app/**/*.scss', '!app/bower_components/**/*.scss'], ["injectSass"]);
});
//This is currently not used. But you can enable by uncommenting
// " //return gulp.src([basePath+ext,...excludeDirs])" above the return.
var excludeDirs = [`!${basePath}/bower_components/${ext}`,`!${basePath}/images/${ext}`]
/**
*
* Enable for advanced use:
*
*
*/
gulp.task('injectSass', function () {
/* Original creator: David Vega. I just modified
* it to take advantage of the Polymer 1.1's shared styles.
*
* This will look all the files that are inside:
* app/elements folder. You can change this to match
* your structure. Note, this gulp script uses convention
* over configuration. This means that if you have a file called
* my-element-styles.html you should have a file called
* my-element-styles.scss
*
* Note #2:
* We use "!" (Exclamation Mark) to exclude gulp from searching these paths.
* What I'm doing here, is that Polymer Starter Kit has inside its app folder
* all the bower dependencies (bower_components). If we don't specify it to
* exclude this path, this will look inside bower_components and will take a long time
* (around 7.4 seconds in my machine) to replace all the files.
*/
//Uncomment if you want to specify multiple exclude directories. Uses ES6 spread operator.
//return gulp.src([basePath+ext,...excludeDirs])
return gulp.src([basePath+ext, '!'+excludeDir+ext])
.pipe(map(function (file, cb) {
//This will match anything between the Start Style and End Style HTML comments.
var startStyle = "<!-- Start Style -->";
var endStyle = "<!-- End Style -->";
//Creates the regEx this ways so I can pass the variables.
var regEx = new RegExp(startStyle+"[\\s\\S]*"+endStyle, "g");
// Converts file buffer into a string
var contents = file.contents.toString();
//Checks if the RegEx exists in the file. If not,
//don't do anything and return.
//Rewrote the if for reduced nesting.
if (!regEx.test(contents)) {
//Return empty. if we return cb(null, file). It will add
//the file that we do not want to the pipeline!!
return cb();
}
/**
* Getting scss
* This will get the .html file that matches the current name
* This means that if you have my-app.component.html
* this will match my-app.component.scss. Replace with .sass if you
* have .sass files instead.
*/
var scssFile = file.path.replace(/\.html$/i, '.scss');
fs.readFile(scssFile, function (err, data) {
//Rewrote the if for reduced nesting.
//If error or there is no Sass, return null.
if (err || !data) {
return cb();
}
nodeSass.render({
data: data.toString(),
includePaths: [path.join('app', 'style/'), ...includePaths],
outputStyle: 'compressed'
}, function (err, compiledScss) {
//Rewrote the if for reduced nesting.
//If error or there is no Sass, return null.
if (err || !compiledScss)
return cb();
/**
* What we are doing here is simple:
* We are re-creating the start and end placeholders
* that we had and inject them back to the .html file
*
* This will allow us to re-inject any changes that we
* do to our .scss or files.
*
*/
var injectSassContent = startStyle +
"<style>" +
compiledScss.css.toString() +
"</style>" +
endStyle;
//This is going to replace everything that was between the <!-- Start Style --> and
// "<!-- End Style -->"
file.contents = new Buffer(contents.replace(regEx, injectSassContent), 'binary');
//This return is necessary, or the modified map will not be modified!
return cb(null,file);
});
});
}))
.pipe(gulp.dest(basePath));
}); //Ends
1) Setup your element:
Suppose you have an element called "hero-tournament":
<dom-module id="hero-tournament">
<template>
<style>
</style>
</template>
<script>
(function() {
'use strict';
Polymer({
is: 'hero-tournament',
});
})();
</script>
</dom-module>
And you want to inject your .scss file into it.
Create besides it two new files:
hero-tournament-style.html
hero-tournament-style.scss
Inside the first file, hero-tournament-style.html write the following:
<!-- hero-tournament-style.html -->
<dom-module id="hero-tournament-style">
<template>
<!-- Start Style -->
<style>
</style>
<!-- End Style -->
</template>
</dom-module>
Note the:
<!-- Start Style --> <!-- End Style -->
comments?
These are SUPER important, as all the css will go inside these ones. These are case sensitive, but not position sensitive. Be sure to include them inside your template tags and outside of your style tags.
Then on your hero-tournament-style.scss file, include your sass' css:
(Example)
.blank{
display: none;
}
2) Run Gulp:
gulp watchSass
Bam! You'll see that your "hero-tournament-style.scss" file will be overwritten with your css!!!
<!-- -hero-tournament-style.html -->
<dom-module id="-hero-tournament-style">
<template>
<!-- Start Style -->
<style>.blank{display:none}
</style><!-- End Style -->
</template>
</dom-module>
Now, you can refer that file anywhere!!! Remember your first element, the original one ("hero-tournament.html")? Do the following to it:
<!-- import the module -->
<link rel="import" href="../path-to-my-element/.html">
<dom-module id="hero-tournament">
<template>
<!-- include the style module by name -->
<style include="hero-tournament-styles"></style>
</template>
<script>
(function() {
'use strict';
Polymer({
is: 'hero-tournament',
});
})();
</script>
</dom-module>
Some last notes:
Using SASS Imports
Using Sass imports is easy, just need to watch out for the following:
In the gulpfile there is a variable called: "includePaths". It's an array in which nodeSass will look for all the imports. Failing to specify your import in any of the mentioned places, will prevent your file from injecting and compiling. By default, in the script there is a 'app/style' directory which will look for it.
Folder structure
Folder structure is important, and it can be adapted as your liking.
This assumes that your elements are inside an "app" folder brother to your gulpfile (In the same hierarchy):
-gulpfile.js
/app
/element
/hero-tournament
-hero-tournament.html
-hero-tournament-styles.html
-hero-tournament-styles.scss
/maybe-other-folder
If you want to change your folder structure, change the "basePath" variable. Be sure to check for leading "/" so you don't mess up your structure!
How do I run my gulpfile?
It's easy:
Call the "watchSass" method for watching, or "injectSass" for using it once.
gulp watchSass
gulp injectSass
More information in the github page!!!
In Polymer 2.0 it's also possible to just import a stylesheet inside the element's template like that:
<dom-module id="your-module-name">
<template>
<style><!-- you can also add additional styling in here --></style>
<link rel="stylesheet" href="link_to_stylesheet.css">
<!-- your template in here -->
</template>
<script>
//your element class + registration here
</script>
</dom-module>
Inside of the stylesheet you can style your content just like in the style-tag. The styles only affect the element and its content.
If you want to use SASS, Stylus, LESS or anything like that, you just have to use a middleware (HOWTO: Stack Overflow) in Express that renders the SASS-Code into CSS on every request. I prefer this solution over GULP/GRUNT task, because I think it's way easier, because you don't always have to run the Task, because of the Middleware it's compiling automatically whenever it's needed.
I hope that helps you
I am using cssmin on files containing #imports. cssmin is recursively importing local files correctly, but for imports that point to a URL the imports are left inline. This makes the resulting minified CSS invalid because # rules must be at the beginning of the file. Does anyone know a good solution or workaround to this problem?
I have exactly the same problem with cssmin and #import, and i found a solution with grunt concat:
Create a concat grunt task that:
Put #import url in the begining of mified css file and replaces references of #imports url for "".
Execute task concat:cssImport after cssmin task.
Grunt task Code: to execute (concat:cssImport)
grunt.initConfig({
concat: {
cssImport: {
options: {
process: function(src, filepath) {
return "#import url(http://fonts.googleapis.com/css?family=Roboto:400,300,900);"+src.replace('#import url(http://fonts.googleapis.com/css?family=Roboto:400,300,900);', '');
}
}
},
files: {
'your_location_file_origin/file.full.min.css': ['your_location_file_destination/file.full.min.css']
}
} )}
My inspiration comes from https://github.com/gruntjs/grunt-contrib-concat#custom-process-function.
I added the processImport: false option to grunt.
'cssmin': {
'options': {
'processImport': false
}
}
Use the following process:
Install node-less
import the files by compiling with less first
minify with cssmin
References
node-less
LESS compile error
I had something like this in the styles.scss:
#import url(custom-fonts.css);
My problem was the #import wasn't able to find the files because the root path was missing. Here's what I did with yeoman angular generator Gruntfile.js config:
cssmin: {
options: {
root: '<%= yeoman.dist %>/styles/'
}
},
Useful link grunt-contrib-cssmin issue #75
I know this question for a very long time but i post this for anybody that looking for this issue on stack overflow ... just put your code in /!..../ like this:
/*! * #import url('//fonts.googleapis.com/cssfamily=Roboto:300,400,400i,500,700,700i'); */
it will be include in your destination min css but don't forget to use remote import in top of your page.
Putting the imports at the top of my scss didn't work for me,I ended up importing the external stylesheets directly from the html:
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet">
<link href="http://fonts.googleapis.com/css?family=Roboto:400,300"
rel="stylesheet">
<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
<!-- build:css(.) styles/vendor.css -->
<!-- bower:css -->
......
<!-- endbower -->
<!-- endbuild -->
<!-- build:css(.tmp) styles/app.css -->
<link el="stylesheet" href="../bower_components/bootstrap/dist/css/bootstrap.css">
<link rel="stylesheet" href="styles/app.css">