I'm trying to get Grunt to process my less files every time I make a change to one of the files.
I have a 'watch' task working, and it says it is processing files when I make a change, and it outputs the correct files which change, so my watch is working, but the changes I make are not being made on the css file.
My file structure is like this
-styles
globals.less
components.less
-menu
menu.less
-header
header.less
The components.less file is a group of less mixins to grab the other .less files like #import "menu/menu.less";
In my grunt file, I have
less: {
development: {
options: {
paths: ['./app/vendor/modern-touch-less','./app/styles']
},
files: {
'./app/styles/modern-touch.css':'./app/vendor/modern-touch-less/style.less',
'./app/styles/components.css':'./app/styles/components.less'
}
}
},
watch: {
js: {
files: ['/scripts/{,*/}*.js','/styles/{,*/}*.less'],
tasks: ['newer:jshint:all, less'],
options: {
livereload: true
}
},
styles: {
files: ['./styles/{,*/}*.*','./vendor/{,*/}*.less'],
tasks: ['less','newer:copy:styles', 'autoprefixer'],
options: {
nospawn: true,
livereload: true
}
},
}
When I first start the app, the less files are built into the correct components.css file, but after changing a file, the components.css file is not updated.
Grunt is started with
grunt.task.run([
'clean:server',
'bower-install',
'concurrent:server',
'less',
'autoprefixer',
'connect:livereload',
'watch',
'karma'
]);
Perhaps you can try simplifying your configuration, by splitting tasks out to run on the appropriate set of files.
watch: {
js: {
files: ['/scripts/{,*/}*.js'],
tasks: ['newer:jshint:all'],
options: {
livereload: true
}
},
styles: {
files: ['./styles/{,*/}*.*','./vendor/{,*/}*.less'],
tasks: ['less','newer:copy:styles', 'autoprefixer'],
options: {
nospawn: true,
livereload: true
}
},
}
In your original watch config, the watch.js.tasks array was ['newer:jshint:all, less'], which looks like a typo, should be ['newer:jshint:all', 'less']. This may fix the problem too, but I would consider just running tasks on the files that they are affected by.
Related
I want to integrate responsive-loader into my Nuxt.js project which runs in SPA mode. (Optional I want to add Vuetify Progressive Image support also).
It will be a static hosting with Netlify.
Versions:
"nuxt": "^2.3.4"
"responsive-loader": "^1.2.0"
"sharp": "^0.21.1"
I found some solutions how to do it (https://stackoverflow.com/a/51982357/8804871) but this is not working for me.
When I run npm run build
I get an error message: "TypeError: Cannot set property 'exclude' of undefined"
My build section looks the following:
build: {
transpile: [/^vuetify/],
plugins: [
new VuetifyLoaderPlugin()
],
extractCSS: true,
/*
** Run ESLint on save
*/
extend(config, { isDev, isClient, isServer }) {
// Default block
if (isDev && isClient) {
config.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /(node_modules)/
})
}
if (isServer) {
config.externals = [
nodeExternals({
whitelist: [/^vuetify/]
})
]
}
// Default block end
// here I tell webpack not to include jpgs and pngs
// as base64 as an inline image
config.module.rules.find(
rule => rule.loader === "url-loader"
).exclude = /\.(jpe?g|png)$/;
/*
** Configure responsive-loader
*/
config.module.rules.push({
test: /\.(jpe?g|png)$/i,
loader: "responsive-loader",
options: {
min: 350,
max: 2800,
steps: 7,
placeholder: false,
quality: 60,
adapter: require("responsive-loader/sharp")
}
});
}
}
The error is probably found in this section:
config.module.rules.find(
rule => rule.loader === "url-loader"
).exclude = /\.(jpe?g|png)$/;
Like said I get this error message: "TypeError: Cannot set property 'exclude' of undefined".
I run this project along with vuetify. I also would like to enable the Progressive image support together with responsive loader. Does anybody know how to setup both rules together?
https://github.com/vuetifyjs/vuetify-loader#progressive-images
The easiest way to integrate responsive-loader into a Nuxt.js project is to use this module: https://www.npmjs.com/package/nuxt-responsive-loader
Disclaimer: I created the module
The problem with your config that it relies on rule.loader property but rule can be defined in use or oneOf config sections as well.
Another one problem is that nuxt internal config has several rules with url-loader(for images, videos, fonts ...).
In your case the rule, you tried to find, has use section and url-loader is defined there, that's why your find function found nothing and threw this error:
{
"test": /\.(png|jpe?g|gif|svg|webp)$/,
"use": [{
"loader": "url-loader",
"options": {
"limit": 1000,
"name": "img/[hash:7].[ext]"
}
}]
}
About responsive-loader, you should remove extensions you want to process with responsive-loader from url-loader rule to avoid unexpected behavior and conflicts, here is extend function working example:
extend(config, ctx) {
let imgTest = '/\\.(png|jpe?g|gif|svg|webp)$/';
// find by reg ex string to not rely on rule structure
let urlRule = config.module.rules.find(r => r.test.toString() === imgTest);
// you can use also "oneOf" section and define both loaders there.
// removed images from url-loader test
urlRule.test = /\.(svg|webp)$/;
config.module.rules.push({
test: /\.(png|jpe?g|gif)$/,
loader: "responsive-loader",
options: {
// place generated images to the same place as url-loader
name: "img/[hash:7]-[width].[ext]",
min: 350,
max: 2800,
steps: 7,
placeholder: false,
quality: 60,
adapter: require("responsive-loader/sharp")
}
})
}
Yes, it looks dirty, but I think it's only way for now to change some loader.
What about vuetify - I think both loaders will conflict with each other and probably the solution is to use single loader that will work with your images.
Hope it helps.
Update for Nuxt >= 2.4.0:
They modified the rules array please update the following line:
let imgTest = '/\\.(png|jpe?g|gif|svg|webp)$/i';
Then the code should work normally again.
I've set Grunt to compile compass with watch task and now I'd like to copy useful files from bower_components/... - e.g. bower_components/jquery/jquery.min.js, because bower produces a lot of unnecessary files, which I want to get rid of, when uploading to server.
CMD produces warning and stops process;
Reading C:\Users\sjiamnocna\Documents\NetBeansProjects\PM_new\node_modules\grunt-bowercopy\package.json...OK
Parsing C:\Users\sjiamnocna\Documents\NetBeansProjects\PM_new\node_modules\grunt-bowercopy\package.json...OK
Reading bower.json...OK
Parsing bower.json...OK
Loading "bowercopy.js" tasks...OK
+ bowercopy
Loading "gruntfile.js" tasks...OK
+ default, dog
No tasks specified, running default tasks.
Running tasks: default
Running "default" task
Running "bowercopy" task
Running "bowercopy:copythat" (bowercopy) task
Verifying property bowercopy.copythat exists in config...OK
File: [no files]
Options: srcPrefix="bower_components", destPrefix="files", ignore=[], report, runBower=false, clean=false, copyOptions={}
Using srcPrefix: bower_components
Using destPrefix: files
Warning: Nothing was copied for the "copythat" target Use --force to continue.
My gruntfile:
module.exports = function (grunt) {
grunt.initConfig({
watch: {
scss: {
files: ['files/style/sass/*.scss'],
tasks: ['compass']
}
},
compass: {
dist: {
options: {
sassDir: 'files/style/sass',
cssDir: 'files/style',
environment: 'development'
}
}
},
bowercopy: {
copythat: {
options: {
runBower: false,
srcPrefix: 'bower_components',
destPrefix: 'files'
},
script: {
'jquery/dist/jquery.min.js': 'script/lib/jquery.min.js',
'jquery-ui/jquery-ui.min.js': 'script/lib/jquery-ui.min.js',
'masonry/dist/masonry.pkgd.min.js': 'script/lib/masonry.pkgd.min.js',
'sweetalert/dist/sweetalert.min.js': 'script/lib/sweetalert.min.js'
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-compass');
grunt.loadNpmTasks('grunt-bowercopy');
grunt.registerTask('default', ['bowercopy']);
grunt.registerTask('dog', ['watch']);
};
Can anyone tell me what's wrong? Or, is there any other way to do it with grunt (automatically :) )?
Thanks #cartant for comment, it was one of the mistakes I've made - using whatever instead of "files"
I've changed position of resource and target
Wrong:
'source':'target'
improved:
'target':'source'
Works!
BEFORE, I was using r.js to optimize and minify my javascript successfully. I had a main.js file that looked something like this:
require.config({
baseUrl: "scripts/lib",
paths: {
jquery: "http://code.jquery.com/jquery-1.11.2.min",
underscore: "https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min",
d3: "d3-for-development",
katex: "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min", // or 0.2.0
mathjax: "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML&delayStartupUntil=configured",
etc...
},
shim: {
underscore: { exports: "_" },
chosen: { deps: ["jquery"] },
mathjax: {
exports: "MathJax",
init: function (){
MathJax.Hub.Config({
tex2jax: {
inlineMath: [['$','$'], ['\\(','\\)']],
processEscapes: true,
},
});
MathJax.Hub.Startup.onload();
return MathJax;
}
},
},
});
require( [
"jquery",
"underscore",
"browser-detect",
"check-types",
"katex",
"mathjax",
etc
], function(
$,
_,
browser,
check,
katex,
mathjax,
etc
){
/////////////////////////// INITIALIZATION ///////////////////////////
loginInit()
show('#login')
etc...
and I could successfully run node build/r.js -o mainConfigFile=www/scripts/main.js baseUrl=www/scripts/lib name=../main out=www/scripts/main-optimized.min.js generateSourceMap=true preserveLicenseComments=false optimize=uglify2 to minify. Everything worked.
NOW, I have a config.js file that looks like this:
require.config({
urlArgs: "bust=" + new Date().getTime(),
baseUrl: "scripts/lib",
paths: {
jquery: ["jquery-min", "http://code.jquery.com/jquery-1.11.2.min"],
underscore: ["underscore-min", "https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min"],
d3: "d3-for-development", // if we add patches separately, then we can just use https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min
katex: ["katex-min", "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.3.0/katex.min"], // or 0.2.0
mathjax: "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML&delayStartupUntil=configured",
main: "../main",
etc...
},
shim: {
underscore: { exports: "_" },
chosen: { deps: ["jquery"] },
mathjax: {
exports: "MathJax",
init: function init() {
MathJax.Hub.Config({
tex2jax: {
inlineMath: [['$', '$'], ['\\(', '\\)']],
processEscapes: true }
});
MathJax.Hub.Startup.onload();
return MathJax;
}
}
}
});
require(["main"], function (main) {
// pass. by loading main, we run main.js
});
Instead of passing the minify/optimize arguments straight into the command line, I've created a rbuild.js file for that:
({
mainConfigFile: "../www/scripts/config.js",
baseUrl: "../www/scripts/lib",
name: "../config",
out: "../www/scripts/config-optimized.min.js",
generateSourceMap: true,
preserveLicenseComments: false, // this is necessary for generateSourceMap to work
optimize: "uglify2",
// removeCombined: true,
// findNestedDependencies: true,
paths: {
// https://github.com/jrburke/requirejs/issues/791
// http://www.anthb.com/2014/07/04/optimising-requirejs-with-cdn-fallback
jquery: "jquery-min",
underscore: "underscore-min",
d3: "d3-for-development",
katex: "katex-min",
mathjax: "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML&delayStartupUntil=configured",
marked: "marked",
chosen: "chosen-min",
jsnetworkx: "jsnetworkx-min",
main: "../main",
},
})
and I run it with node build/r.js -o build/rbuild.js in the command line. It appears to run successfully and makes the config-optimized.min.js file, as expected. The output is:
Tracing dependencies for: ../config
Cannot optimize network URL, skipping: http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML&delayStartupUntil=configured
Uglify2 file: /Users/Matthew/programming/prove-math/www/scripts/config-optimized.min.js
/Users/Matthew/programming/prove-math/www/scripts/config-optimized.min.js
----------------
/Users/Matthew/programming/prove-math/www/scripts/lib/jquery-min.js
/Users/Matthew/programming/prove-math/www/scripts/lib/underscore-min.js
/Users/Matthew/programming/prove-math/www/scripts/lib/browser-detect.js
/Users/Matthew/programming/prove-math/www/scripts/lib/check-types.js
/Users/Matthew/programming/prove-math/www/scripts/lib/katex-min.js
/Users/Matthew/programming/prove-math/www/scripts/lib/profile.js
/Users/Matthew/programming/prove-math/www/scripts/lib/marked.js
/Users/Matthew/programming/prove-math/www/scripts/lib/d3-for-development.js
/Users/Matthew/programming/prove-math/www/scripts/lib/user.js
/Users/Matthew/programming/prove-math/www/scripts/lib/graph-animation.js
/Users/Matthew/programming/prove-math/www/scripts/lib/graph.js
/Users/Matthew/programming/prove-math/www/scripts/lib/node.js
/Users/Matthew/programming/prove-math/www/scripts/lib/blinds.js
/Users/Matthew/programming/prove-math/www/scripts/lib/chosen-min.js
/Users/Matthew/programming/prove-math/www/scripts/main.js
/Users/Matthew/programming/prove-math/www/scripts/lib/../config.js
But when I visit index.html via my server, the page is blank. The JS console gives no errors or log messages, which suggests that no JS is being run. My server gives no errors, which suggests that everything has been sent to the client successfully, and the client JS is not running.
So I'm pretty convinced the JS is there but not running. Is there something wrong with my setup that causes config.js to not run the code? With no error messages, I am having trouble troubleshooting :)
So I commented out
generateSourceMap: true,
preserveLicenseComments: false, // this is necessary for generateSourceMap to work
optimize: "uglify2",
and it worked! THEN, I uncommented that stuff, and it STILL worked!
It seems that as of requireJS 2.2 (I was using RequireJS 2.1.6 BEFORE), you can now use
optimize: "uglify",
or nothing at all, since this is the default setting. As of requireJS 2.2, it DOES use uglify2 in this case. This is the closest thing to an explanation that I can give :/
I am almost absolutely new to grunt.
I used grunt with bootstrap 3.1.1 and the grunt watch-command worked out great. My bootstrap.min.css file was compiled every time. (i upload my bootstrap.min.css)
Later on i lost my 3.1.1 grunt file (long story short my computer crashed).
So now with Bootstrap 3.2.0 i was going to restablish my grunt-work-flow.
But now when i use grunt watch i only get the "bootstrap.css" and "bootstrap.theme.css" compiled.
I have spent the last hours to figure this out without success.
WHAT I WANT
I want grunt watch to compile the minified "bootstrap.min.css"
So how do i call the min.css-function on the watch?
I will be glad to get some help.
Grunt watch will only watch the files then run tasks you have set. I am assuming in your gruntfile you ahve something like this:
css: {
files: [
'bootstrap.css',
],
tasks: ['less'],
},
In the less task you should have something like below. Note the cleancss option being set to true:
options: {
cleancss: true
},
files: {
"dest/bootstrap.min.css": "src/bootstrap.css",
"dest/bootstrap.theme.min.css": "src/bootstrap.theme.css"
}
UPDATE:
Based on the file you uploaded you should be running the cssmin:core task when the watch is triggered.
UPDATE 2:
To update the watch task you can just add the cssmin:core task to the less subtask:
less: {
files: 'less/**/*.less',
tasks: ['less', 'cssmin:core]
}
Here you are telling it to run the less task, followed by the cssmin task whenever one of the less files is changed while watching.
Your gruntfile.js will have a 'watch' section as below
watch: {
src: {
files: '<%= jshint.core.src %>',
tasks: ['jshint:src', 'qunit', 'concat']
},
test: {
files: '<%= jshint.test.src %>',
tasks: ['jshint:test', 'qunit']
},
less: {
files: 'less/**/*.less',
tasks: 'less'
}
},
The tasks under less subsection define the tasks that 'grunt watch' will run. In Bootstrap 3.3.2 (I guess in 3.1.1 also it would be the same) is the 'cssmin' task that minifies the core bootstrap css. So you need to add the task to less so the code above becomes
watch: {
src: {
files: '<%= jshint.core.src %>',
tasks: ['jshint:src', 'qunit', 'concat']
},
test: {
files: '<%= jshint.test.src %>',
tasks: ['jshint:test', 'qunit']
},
less: {
files: 'less/**/*.less',
tasks: ['less', 'cssmin']
}
},
None of the above worked for me. Looks like the css minification command changed to cssmin:minifyCore so I have updated my watch task to the following:
watch: {
src: {
files: '<%= jshint.core.src %>',
tasks: ['jshint:core', 'qunit', 'concat']
},
test: {
files: '<%= jshint.test.src %>',
tasks: ['jshint:test', 'qunit']
},
less: {
files: 'less/**/*.less',
tasks: ['less', 'cssmin:minifyCore']
}
},
Hopefully this helps others!
related
I was able to set up a Grunt task to SFTP files up to my dev server using grunt-ssh:
sftp: {
dev: {
files: {
'./': ['**','!{node_modules,artifacts,sql,logs}/**'],
},
options: {
path: '/path/to/project',
privateKey: grunt.file.read(process.env.HOME+'/.ssh/id_rsa'),
host: '111.111.111.111',
port: 22,
username: 'marksthebest',
}
}
},
But this uploads everything when I run it. There are thousands of files. I don't have time to wait for them to upload one-by-one every time I modify a file.
How can I set up a watch to upload only the files I've changed, as soon as I've changed them?
(For the curious, the server is a VM on the local network. It runs on a different OS and the setup is more similar to production than my local machine. Uploads should be lightning quick if I can get this working correctly)
What you need is grunt-newer, a task designed especially to update the configuration of any task depending on what file just changed, then run it. An example configuration could look like the following:
watch: {
all: {
files: ['**','!{node_modules,artifacts,sql,logs}/**'],
tasks: ['newer:sftp:dev']
}
}
You can do that using the watch event of grunt-contrib-watch.
You basically need to handle the watch event, modify the sftp files config to only include the changed files, and then let grunt run the sftp task.
Something like this:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
secret: grunt.file.readJSON('secret.json'),
watch: {
test: {
files: 'files/**/*',
tasks: 'sftp',
options: {
spawn: false
}
}
},
sftp: {
test: {
files: {
"./": "files/**/*"
},
options: {
path: '/path/on/the/server/',
srcBasePath: 'files/',
host: 'hostname.com',
username: '<%= secret.username %>',
password: '<%= secret.password %>',
showProgress: true
}
}
}
}); // end grunt.initConfig
// on watch events configure sftp.test.files to only run on changed file
grunt.event.on('watch', function(action, filepath) {
grunt.config('sftp.test.files', {"./": filepath});
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-ssh');
};
Note the "spawn: false" option, and the way you need to set the config inside the event handler.
Note2: this code will upload one file at a time, there's a more robust method in the same link.
You can achieve that with Grunt:
grunt-contrib-watch
grunt-rsync
First things first: I am using a Docker Container. I also added a public SSH key into my Docker Container. So I am uploading into my "remote" container only the files that have changed in my local environment with this Grunt Task:
'use strict';
module.exports = function(grunt) {
grunt.initConfig({
rsync: {
options: {
args: ['-avz', '--verbose', '--delete'],
exclude: ['.git*', 'cache', 'log'],
recursive: true
},
development: {
options: {
src: './',
dest: '/var/www/development',
host: 'root#www.localhost.com',
port: 2222
}
}
},
sshexec: {
development: {
command: 'chown -R www-data:www-data /var/www/development',
options: {
host: 'www.localhost.com',
username: 'root',
port: 2222,
privateKey: grunt.file.read("/Users/YOUR_USER/.ssh/id_containers_rsa")
}
}
},
watch: {
development: {
files: [
'node_modules',
'package.json',
'Gruntfile.js',
'.gitignore',
'.htaccess',
'README.md',
'config/*',
'modules/*',
'themes/*',
'!cache/*',
'!log/*'
],
tasks: ['rsync:development', 'sshexec:development']
}
},
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-rsync');
grunt.loadNpmTasks('grunt-ssh');
grunt.registerTask('default', ['watch:development']);
};
Good Luck and Happy Hacking!
I have recently ran into a similar issue where I wanted to only upload files that have changed. I'm only using grunt-exec. Providing you have ssh access to your server, you can do this task with much greater efficiency. I also created an rsync.json that is ignored by git, so collaborators can have their own rsync data.
The benefit is that if anyone makes a change it automatically uploads to their stage.
// Watch - runs tasks when any changes are detected.
watch: {
scripts: {
files: '**/*',
tasks: ['deploy'],
options: {
spawn: false
}
}
}
My deploy task is a registered task that compiles scripts then runs exec:deploy
// Showing exec:deploy task
// Using rsync with ssh keys instead of login/pass
exec: {
deploy: {
cmd: 'rsync public_html/* <%= rsync.options %> <%= rsync.user %>#<%= rsync.host %>:<%=rsync.path %>'
}
You see a lot of the <%= rsync %> stuff? I use that to grab info from rysnc.json which is ingored by git. I only have this because this is a team workflow.
// rsync.json
{
"options": "-rvp --progress -a --delete -e 'ssh -q'",
"user": "mmcfarland",
"host": "example.com",
"path": "~/stage/public_html"
}
Make sure you rsync.json is defined in grunt:
module.exports = function(grunt) {
var rsync = grunt.file.readJSON('path/to/rsync.json');
var pkg = grunt.file.readJSON('path/to/package.json');
grunt.initConfig({
pkg: pkg,
rsync: rsync,
I think it's not good idea to upload everything that changed at once to staging server. And working on the staging server is not a good idea too. You have to configure your local machine server, to be the same as staging/production
It's better to upload 1 time, when you do deployment.
You can archive all the files using grunt-contrib-compress. And push them using grunt-ssh as 1 file, then extract it on the server, that will be much faster.
that's example of compress task:
compress: {
main: {
options:{
archive:'build/build.tar.gz',
mode: 'tgz'
},
files: [
{cwd: 'build/', src: ['sites/all/modules/**'], dest:'./'},
{cwd: 'build/', src: ['sites/all/themes/**'], dest:'./'},
{cwd: 'build/', src: ['sites/default/files/**'], dest:'./'}
]
}
}
PS: Didn't ever look to rsync grunt modules.
I understand that it's might not what you are looking for. But i decided to create my answer as standalone answer.