I'm very new to grunt. As I was reading examples I wondered how should I handle next case. Lets assume I have this structure:
/pages/layout.php
/pages/index.php
/pages/contacts.php
Layout contains common js (like jquery/bootstrap) while contacts extends layout.php and adds some new js
So, layout uses
jquery.js
bootstrap.js
contacts.php
jquery.js
bootstrap.js
feedback.js
In articles I read the authors usually would create only one file, like frontend.js then uglify it. But if I have multiple js-plugins combination for each page, am I right that my concat configuration should look like:
module.exports = function(grunt) {
// I haven't tested this but that's the way I think it should look
// Common scripts which will be used on every page
var layout = ['/path/to/jquery.js', '/path/to/bootstrap.js'];
grunt.initConfig({
concat: {
options: {
separator: "\n"
},
layout: {
src: layout.concat(['/path/to/index.js']),
dest: '/path/to/result'
},
contacts: {
src: layout.concat(['/path/to/feedback.js']),
dest: '/path/to/result'
}
}
});
}
The config might get quite big, depending on how much pages (which use js) I have. Or there is another approach to do such thing?
Related
I want to organize my HTML, JS, and LESS by module. I'm already using Grunt to compile *.js and *.html from my source folders.
So I configured grunt as follows:
grunt.initConfig({
less: {
ALL: {
files: { 'compiled.css': '**/*.less' }
}
}
}
But this runs into a major problem: constants and mixins from my /helper/*.less files are not accessible to other .less files.
It seems like grunt-contrib-less compiles each individual .less file, and then combines the output, but doesn't compile anything "globally".
The only solution I can think of is to create and maintain a master.less that #imports each individual .less file. But I'm trying to achieve an extremely modular build process, and I don't have to list any HTML or JS files, so I'm really hoping to find a *.less solution too!
Thanks to #seven-phases-max for the following answer!
less-plugin-glob
Allows you to use wildcards in #import statements! Works perfectly!
// master.less
#import "helpers/**/*.less";
#import "modules/**/*.less";
And all you need to add to your Grunt configuration is the plugins option:
// Gruntfile.js
grunt.initConfig({
less: {
'MASTER': {
src: 'master.less',
dest: 'master.css',
options: {
plugins: [ require('less-plugin-glob') ]
}
}
}
});
And, don't forget, npm install less-plugin-glob.
Here's one way to achieve an effortless development experience.
However, it requires a generated file and a custom task.
Auto-generate the master.less file
Create a task that generates master.less by writing an #import statement for each *.less file:
grunt.registerTask('generate-master-less', '', function() {
generateFileList({
srcCwd: 'modules',
src: '**/*.less',
dest: 'less/master.less',
header: '// THIS FILE IS AUTOMATICALLY GENERATED BY grunt generate-master-less\n',
footer: '// THIS FILE IS AUTOMATICALLY GENERATED BY grunt generate-master-less\n',
template: '#import "<%= filename %>";\n',
join: ''
});
});
function generateFileList(options) {
var _ = grunt.util._;
var files = grunt.file.expand({ cwd: options.srcCwd }, options.src);
var results = files.map(function (filename) {
return _.template(options.template, { 'filename': filename });
});
var result = options.header + results.join(options.join) + options.footer;
grunt.file.write(options.dest, result);
}
Then, use grunt-contrib-less to just build master.less.
I'm using Webpack with the extract-text-webpack-plugin.
In my project, I have some build scripts. One of the build scripts is supposed to bundle and minify CSS only. As I'm using Webpack for the other scripts, I found it a good idea to use Webpack even when I only want to bundle and minify CSS.
It's working fine, except that I can't get rid of the output.js file. I don't want the resulting webpack output file. I just want the CSS for this particular script.
Is there a way to get rid of the resulting JS? If not, do you suggest any other tool specific for handling CSS? Thanks.
There is an easy way, no extra tool is required.
There is an easy way and you don't need extra libraries except which you are already using: webpack with the extract-text-webpack-plugin.
In short:
Make the output js and css file have identical name, then the css file will override js file.
A real example (webpack 2.x):
import path from 'path'
import ExtractTextPlugin from 'extract-text-webpack-plugin'
const config = {
target: 'web',
entry: {
'one': './src/one.css',
'two': './src/two.css'
},
output: {
path: path.join(__dirname, './dist/'),
filename: '[name].css' // output js file name is identical to css file name
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader'
})
}
]
},
plugins: [
new ExtractTextPlugin('[name].css') // css file will override generated js file
]
}
Unfortunately, that is currently not possible by design. webpack started as a JavaScript bundler which is capable of handling other "web modules", such as CSS and HTML. JavaScript is chosen as base language, because it can host all the other languages simply as strings. The extract-text-webpack-plugin is just extracting these strings as standalone file (thus the name).
You're probably better off with PostCSS which provides various plugins to post-process CSS effectively.
One solution is to execute webpack with the Node API and control the output with the memory-fs option. Just tell it to ignore the resulting js-file. Set the output.path to "/" in webpackConfig.
var compiler = webpack(webpackConfig);
var mfs = new MemoryFS();
compiler.outputFileSystem = mfs;
compiler.run(function(err, stats) {
if(stats.hasErrors()) { throw(stats.toString()); }
mfs.readdirSync("/").forEach(function (f) {
if(f === ("app.js")) { return; } // ignore js-file
fs.writeFileSync(destination + f, mfs.readFileSync("/" + f));
})
});
You can clean up your dist folder for any unwanted assets after the done is triggered. This can be easily achieved with the event-hooks-webpack-plugin
//
plugins: [
new EventHooksPlugin({
'done': () => {
// delete unwanted assets
}
})
]
Good Luck...
My directory structure looks like below:
--myapp/
--client/
--build/
--css/
--js/
--angular/ #angular libraries here
--angular.js
--angular-resource.js
--angular-ui-router.js
--jquery/ #jquery libraries here
--jquery.js
--app.js
--index.html
--server/ #All server related files here
--Gruntfile.js
My Grunt file so far looks like this
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json');
concat: {
dist: {
src: ['client/js/angular/*.*]
dest: 'client/build/angular-build.js'
}
}
});
};
This is as far as I have gotten. Don't see any easy grunt file tutorials on this.
Th ouput I am looking for is all angular libraries in one output file. All jquery libraries in another.. all css libraries in third.
How do i achieve this ?
A few things:
You don't know it, but you're using grunt-contrib-concat, so you'd better npm install that and grunt.loadNpmTasks('grunt-contrib-concat'); it.
Concat doesn't support wildcards. This is good, because the order that these files are included is going to matter. angular.js must be included before angular-resource.js
I've included a rough template of what your Gruntfile needs to look like to accomplish this, but know that this is not the right way to do this. You should be loading your dependencies using something like bower and serving them individually, or using something like Browserify or Require.js. You'll come to see the value of that as the project progresses.
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json');
concat: {
angular: {
src: [
'client/js/angular/angular.js',
'client/js/angular/angular-resource.js',
'client/js/angular/angular-ui-router.js'
]
dest: 'client/build/angular-build.js'
},
jquery: {
src: ['client/js/jquery/jquery.js']
dest: 'client/build/jquery-build.js'
},
...
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
};
I want to add a grunt task (specifically angular-template) to my lineman application.js file. There is some documentation found here and here. However, it just tells me to add the grunt task to loadNpmTasks. The problem is that from a fresh project created using lineman, my application.js file does not have a loadNpmTasks array, nor do the comments point out where I should put it. Both examples I have found in the documentations do not show what the application.js file should look like in it's entirety.
The application.js file should look something like this (obviously the src/dest options are not configured correctly:
module.exports = function(lineman) {
return {
loadNpmTasks:['grunt-angular-templates'],
ngtemplates: {
app: {
src: '**.html',
dest: 'templates.js'
}
}
};
};
Then to run the task:
lineman grunt ngtemplates
I have an html file that contains references to js files. I want to parse it extract a list of referenced js files and feed contrib-concat or any other task with them.
Is there any convenient way to use output of one grunt task as an input for another?
Use grunt.config. Here is an example:
grunt.initConfig({
concat: {
js: {
src: ['default/concat/files/*'],
dest: ['dist/javascript.js'],
},
},
});
grunt.registerTask('extractjs', function() {
/* Do the js extraction */
// Overwrite the concat.js.src with your extracted files.
grunt.config(['concat', 'js', 'src'], extractedFiles);
});
So now when you run grunt extractjs concat it will extract the js and then concat the extracted js files. Check out this task: https://github.com/cgross/grunt-dom-munger as he is working on a similar goal. Here is a grunt issue with more examples as well: https://github.com/gruntjs/grunt/issues/747