how to convert every sass file to css automatically with grunt - css

I want to convert all my scss files(located in sass/ folder) to css files(located in css/ folder) automatically on save. And I want it to work even with the new ones. For example, I had index-style.scss and header-style.scss, already converted to css, but if I create another scss file, like footer-style.scss, I want it to convert to css AUTOMATICALLY, without editing Gruntfile or something.
scss and css files will always match
I tried:
files: {
'src/main/webapp/WEB-INF/css/' : 'src/main/webapp/WEB-INF/sass/'
}
there were no compiling errors, but it didn't convert scss file. Also tried :
files: {
'src/main/webapp/WEB-INF/css/*.css' : 'src/main/webapp/WEB-INF/sass/*.scss'
}
Is this even possible?
P.S.
I also tried adding this code to the Gruntfile.js:
let cssDir = "src/main/webapp/WEB-INF/css/";
let sassDir = "src/main/webapp/WEB-INF/sass/";
let filesObj = {}
// filesObj[cssDir+"login-page.css"] = sassDir+"login-page.scss"
let fs = require('fs')
fs.readdir(sassDir, function (err, files) {
if (err) {
console.log("could not list the dir" + err);
process.exit(1);
}
files.forEach(function (file, index) {
console.log("forEach is working!")
let cssPath = cssDir+file;
fs.access(cssPath,(err) => {
console.log("fs.access is working")
if (err) {
fs.appendFile(cssPath, '', function (err) {
console.log("appendFile is working")
if (err) {
console.log("Grunt file 17 line error");
process.exit(1);
}
})
}
})
console.log("\n\n==========================================\n\n"+cssPath+"\n\n"+sassDir+'file\n')
filesObj[cssDir+'file'] = sassDir+'file'
})
})
...
sass: {
dist: {
options: {
sourcemap: false,
compress: false,
yuicompress: false,
style: 'expanded',
},
files: filesObj
},
}
...
but, it didn't help

Related

Gulp and Critical CSS - TypeError: Cannot read property 'content-type' of undefined

I'm running my build in Gulp and keep getting the following error in my terminal when it comes to generating my critical css files.
[08:47:29] -> Generating critical CSS: https://example.com/ -> ./templates/index_critical.min.css
[08:47:43] TypeError: Cannot read property 'content-type' of undefined
What does the error mean?
How do I solve the error?
If this is a syntax issue in my gulp file - is there way to find the line and reference to the issue either in the terminal when running the command or by using a validator?
Below is the gulp task for the critical css along with the tasks for processing the css and sass files which it references.
// scss - build the scss to the build folder, including the required paths, and writing out a sourcemap
gulp.task("scss", () => {
$.fancyLog("-> Compiling scss");
return gulp.src(pkg.paths.src.scss + pkg.vars.scssName)
.pipe($.plumber({errorHandler: onError}))
.pipe($.sourcemaps.init({loadMaps: true}))
.pipe($.sass({
includePaths: pkg.paths.scss
})
.on("error", $.sass.logError))
.pipe($.cached("sass_compile"))
.pipe($.autoprefixer())
.pipe($.sourcemaps.write("./"))
.pipe($.size({gzip: true, showFiles: true}))
.pipe(gulp.dest(pkg.paths.build.css));
});
// css task - combine & minimize any distribution CSS into the public css folder, and add our banner to it
gulp.task("css", ["scss"], () => {
$.fancyLog("-> Building css");
return gulp.src(pkg.globs.distCss)
//.pipe(purgecss({
// content: [pkg.paths.templates + '*.twig']
// }))
.pipe($.plumber({errorHandler: onError}))
.pipe($.newer({dest: pkg.paths.dist.css + pkg.vars.siteCssName}))
.pipe($.print())
.pipe($.sourcemaps.init({loadMaps: true}))
.pipe($.concat(pkg.vars.siteCssName))
.pipe($.if(process.env.NODE_ENV === "production",
$.cssnano({
discardComments: {
removeAll: true
},
discardDuplicates: true,
discardEmpty: true,
minifyFontValues: true,
minifySelectors: true
})
))
.pipe($.header(banner, {pkg: pkg}))
.pipe($.sourcemaps.write("./"))
.pipe($.size({gzip: true, showFiles: true}))
.pipe(gulp.dest(pkg.paths.dist.css))
.pipe($.filter("**/*.css"))
.pipe($.livereload());
});
// Process the critical path CSS one at a time
function processCriticalCSS(element, i, callback) {
const criticalSrc = pkg.urls.critical + element.url;
const criticalDest = pkg.paths.templates + element.template + "_critical.min.css";
let criticalWidth = 1200;
let criticalHeight = 1200;
if (element.template.indexOf("amp_") !== -1) {
criticalWidth = 600;
criticalHeight = 19200;
}
$.fancyLog("-> Generating critical CSS: " + $.chalk.cyan(criticalSrc) + " -> " + $.chalk.magenta(criticalDest));
$.critical.generate({
src: criticalSrc,
dest: criticalDest,
penthouse: {
blockJSRequests: false,
forceInclude: pkg.globs.criticalWhitelist
},
inline: false,
ignore: [],
css: [
pkg.paths.dist.css + pkg.vars.siteCssName,
],
minify: true,
width: criticalWidth,
height: criticalHeight
}, (err, output) => {
if (err) {
$.fancyLog($.chalk.magenta(err));
}
callback();
});
}
// critical css task
gulp.task("criticalcss", ["css"], (callback) => {
doSynchronousLoop(pkg.globs.critical, processCriticalCSS, () => {
// all done
callback();
});
});
// Lean Production build
gulp.task("leanbuild", ["set-prod-node-env", "static-assets-version", "criticalcss"]);
This happens if the src param you pass into critical is invalid, and returns no response.

CRA + Craco + multiple CSS themes

Folder structure:
src/index.tsx
src/themes/dark.scss
src/themes/light.scss
...
craco webpack modifications:
const path = require('path');
module.exports = {
webpack: {
configure: (webpackConfig, { env, paths }) => {
webpackConfig.entry.push(
path.join(process.cwd(), "src/themes/dark.scss"),
path.join(process.cwd(), "src/themes/light.scss")
);
webpackConfig.module.rules.splice(1, 0, {
test: /\.themes\/dark.scss$/,
use: [
{ loader: require.resolve('sass-loader') },
{ loader: require.resolve('css-loader') }
]
});
webpackConfig.module.rules[3].oneOf[5].exclude = /\.(module|themes)\.(scss|sass)$/;
webpackConfig.module.rules[3].oneOf[6].exclude = /\.themes\.(scss|sass)$/;
webpackConfig.module.rules[3].oneOf[7].exclude.push(/\.themes\.(scss|sass)$/);
return webpackConfig;
}
}
};
the intent is I am hoping obvious - we are trying to generate two theme css files from src/themes directory, which will be later changed manually with unloading / loading <link in DOM directly, I was inspired by Output 2 (or more) .css files with mini-css-extract-plugin in webpack and https://github.com/terence55/themes-switch/blob/master/src/index.js.
Now comes the troubles - after build process:
Creating an optimized production build...
Compiled successfully.
File sizes after gzip:
122.24 KB build/static/css/2.2e93dcba.chunk.css
762 B build/static/js/runtime~main.a8a9905a.js
191 B build/static/css/main.d0c4fa77.chunk.css
157 B build/static/js/main.2063d3e0.chunk.js
109 B build/static/js/2.9b95e8c0.chunk.js
(CSS files are OK, there are few generic CSS files and few of them are from libraries). But no theme files... I try to combine with file-loader, but it does not work either.
I would recommend configuring webpack to pack all assets related to a particular theme into a single chunk:
const themeFileRegex = /(\w+)\.theme\.(scss|sass)$/;
// recursively searches for a theme stylesheet in parent issuers
function getIssuerTheme(module) {
const matches = themeFileRegex.exec(module.resource);
if (matches) {
return matches[1];
} else {
return module.issuer && getIssuerTheme(module.issuer);
}
}
...
webpackConfig.optimization.splitChunks.cacheGroups = {
...webpackConfig.optimization.splitChunks.cacheGroups,
themes: {
test: getIssuerTheme,
name: m => {
const name = getIssuerTheme(m);
return `theme.${name}`;
},
reuseExistingChunk: false,
},
};
With that, you should get chunks named theme.light, theme.dark, etc.

Grunt browserify transform ReactJS then bundle

In my project I am using Grunt to build the javascript files, I have ReactJS components which makes Grunt complain about Error: Parsing file in one of my javascript file, caused by the JSX syntax:
"use strict";
var $ = require('jquery');
var React = require('react');
require('app/comments/comment_box_react.js');
$.fn.comment_box = function(options){
var opts = $.extend({}, $.fn.comment_box.defaults, options);
var me = this;
React.render(<CommentBox url="comments.json" />, this);
return me;
}
$.fn.comment_box.defaults = {}
My browerify config in Grunt looks like this:
browserify: {
main: {
files: {
'<%= paths.js_built %>/bundle.js': ['<%=paths.js %>/project.js'],
}
},
transform: ['reactify'],
},
How do I perform transform first before the bundle?
The transform example in their docs has the transform array in an options object.
browserify: {
dist: {
files: {
'build/module.js': ['client/scripts/**/*.js', 'client/scripts/**/*.coffee'],
},
options: {
transform: ['coffeeify']
}
}
}
Also, looks like your transform definition is outside of your main definition. Not sure if that would be global or not, so you might have to move it inside of main. Something like this
browserify: {
main: {
files: {
'<%= paths.js_built %>/bundle.js': ['<%=paths.js %>/project.js'],
},
options: {
transform: ['reactify']
}
}
}
I ended up using gulp and transform globally before bundle:
https://github.com/andreypopp/reactify/issues/66
gulp.task('activitiesjs', function() {
browserify({
entries: [
paths.js+'/lib/activities/activities.js',
]
}).transform(reactify, {global:true}).bundle().pipe(source('bundle.js')).pipe(gulp.dest(paths.js_built+'/activities'));
});

How to execute tasks based on a subfolder with Grunt and Grunt-Watch

I want to be able to have different subprojects inside my main project. For example:
-- my-project/
- Gruntfile.js
- subproject1/
- index.html
- scss/
- main.scss
- subproject2/
- index.html
- scss/
- main.scss
I want to be able to modify a file in subproject1 without triggering subproject2 tasks.
As of right now I'm configuring my gruntfile like so:
watch: {
subproject1: {
files: ['subproject1/*.html', 'subproject1/scss/**/*.scss'],
tasks: ['sass', 'premailer:subproject1']
},
subproject2: {
files: ['subproject2/*.html', 'subproject2/scss/**/*.scss'],
tasks: ['sass', 'premailer:subproject2']
}
},
premailer: {
subproject1: {
options: {
css: 'subproject1/css/main.css',
verbose: false
},
files: [
{
'subproject1/dist/index.html' : 'subproject1/index.html'
}
]
},
subproject2: {
options: {
css: 'subproject2/css/main.css',
verbose: false
},
files: [
{
'subproject2/dist/index.html' : 'subproject2/index.html'
}
]
},
}
Is there a way to dynamically specify to grunt what task to run depending on file modified (eg, I modify folder/index.html, then run premailer:folder) or is this the only way to achieve it ?
You can check all folders in your main folder inside your Gruntfile, using the grunt.file methods (docs here), create an array of subproject names and then using forEach to create your task dynamically.
Something like this should go:
/*global module:false*/
module.exports = function(grunt) {
var mycwd = "./";
var tempFileList = grunt.file.expand(
{ filter: function (src) {
if (grunt.file.isDir(src) == true) {
return true;
}
return false;
} },
[ mycwd + "!(Gruntfile.js|node_modules|package.json)" ] // structure to analyse
);
// I create an empty array to put all elements in, once cleaned.
var fileList = [];
tempFileList.forEach(function(url){
var cwd = url;
cwd = cwd.replace(mycwd, "");
fileList.push(cwd);
})
var watchObject = {};
var premailerObject = {};
fileList.forEach(function(name) {
watchObject[name] = {
files: [name + '/*.html', name + '/scss/**/*.scss'],
tasks: ['sass', 'premailer:' + name]
};
var filesObject = {};
filesObject[name+'/css/main.css'] = name + '/index.html';
premailerObject[name] = {
options: { css: name + '/css/main.css', verbose: false },
files: [ filesObject ]
};
});
var configObject = {
watch: watchObject,
premailer: premailerObject
};
// just to check the final structure
console.log(configObject);
grunt.initConfig(configObject);
};

Grunt filerev uses wrong sources with sourcemapping

I just started using Grunt. I'm trying to uglify a set of files with Grunt, and then create a revision of that file (with grunt-filerev). Everything is fine, except the sourcemaps for the file.
I uglify my files into 1 file called shared.min.js. I also create a sourcemap for this file, and filerev creates a revisioned file and a sourcemap for the revisioned file as well. This means it will generate 4 files:
shared.min.js
shared.min.js.map
shared.min.153987b3.js
shared.min.153987b3.js.map
The problem is that the shared.min.153987b3.js.map uses shared.min.js as it's source, I think it should be shared.min.153987b3.js instead. As you can see in my Gruntfile, I dirty-fixed this with a task called fixFilerevSourcemap.
Then the next problem occured: shared.min.153987b3.js uses shared.min.js.map as it's sourcemap, I think it should be shared.min.153987b3.js.map. Should I dirty-fix this as well? Or is there a better solution?
I have the following Grunt configuration:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
sourceMap: true,
sourceMapIncludeSources: true
},
dist: {
files: {
'somedir/shared.min.js': ['otherdir/file1.js', 'otherdir/file2.js'],
}
}
},
filerev: {
options: {
sourceMap: true
},
scripts: {
src: ['somedir/shared.min.js'],
dest: 'somedir/'
}
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-filerev');
grunt.registerTask('fixFilerevSourcemap', 'Blaat', function() {
var revisionedSource;
for (var file in grunt.filerev.summary) {
var revisionedFile = grunt.filerev.summary[file];
if (revisionedFile.substr(revisionedFile.length - 3) == 'map') {
if (null == revisionedSource) {
throw "No revisionedSource found!";
}
var mapData = grunt.file.readJSON(revisionedFile);
mapData.file = basename(revisionedSource);
grunt.file.write(revisionedFile, JSON.stringify(mapData));
} else {
revisionedSource = basename(revisionedFile);
}
}
});
grunt.registerTask('default', ['uglify', 'filerev', 'fixFilerevSourcemap']);
function basename(path, suffix) {
// discuss at: http://phpjs.org/functions/basename/
// original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// improved by: Ash Searle (http://hexmen.com/blog/)
// improved by: Lincoln Ramsay
// improved by: djmix
// improved by: Dmitry Gorelenkov
// example 1: basename('/www/site/home.htm', '.htm');
// returns 1: 'home'
// example 2: basename('ecra.php?p=1');
// returns 2: 'ecra.php?p=1'
// example 3: basename('/some/path/');
// returns 3: 'path'
// example 4: basename('/some/path_ext.ext/','.ext');
// returns 4: 'path_ext'
var b = path;
var lastChar = b.charAt(b.length - 1);
if (lastChar === '/' || lastChar === '\\') {
b = b.slice(0, -1);
}
b = b.replace(/^.*[\/\\]/g, '');
if (typeof suffix === 'string' && b.substr(b.length - suffix.length) == suffix) {
b = b.substr(0, b.length - suffix.length);
}
return b;
}
};

Resources