Grunt reloading JSON from file and updating task options - javascript

I am using a configuration file with JSON format containing names of other files which I concatenate when one of the files is changed. It works great so far, but I want to make the Grunt to reload the configuration file once it's changed (I remove or add something to the JSON) and update other task options (the concat task).
Here is my simplified Gruntfile.js:
module.exports = function(grunt) {
function getModules() {
var modules = grunt.file.readJSON('src/js/modules.json').modules;
for ( var module in modules ) {
if ( modules.hasOwnProperty(module) ) {
modules[module] = 'src/js/modules/' + modules[module] + '.js';
}
}
modules.push('src/js/scripts.js');
return modules;
}
var modules = getModules();
grunt.initConfig({
concat: {
options: {
separator: ';',
},
dist: {
src: modules,
dest: 'assets/js/scripts.min.js',
},
},
watch: {
js_modules: {
files: ['src/js/modules.json'],
tasks: ['reload_modules', 'concat'],
options: {
spawn: false,
livereload: true,
},
}
}
});
grunt.registerTask('reload_modules', "Reload JavaScript modules", function() {
modules = getModules();
});
};
As you can see I have some attempt to solve my problem, but the updated modules variable is not used in the concat task. The task uses the variable value loaded when the grunt default task is started.

You should be able to overwrite the config using grunt.config.merge:
var config = {
// ...
};
grunt.config.init(config);
grunt.registerTask('reload_modules', "Reload JavaScript modules", function() {
config.concat.options.dist.src = getModules();
grunt.config.merge(config);
});
http://gruntjs.com/api/grunt.config

Related

Grunt - Is it possible to pass global custom functions to external tasks which were loaded by grunt.loadTasks?

I have custom functions that work well when the tasks are defined in the same gruntfile.js but my gruntfile.js now has more than 2000 lines and its becoming hard to maintain. Is it possible to use the functions globally without having to define them again in each task file?. When they are called from the external task neither of the two functions work. I just get error function not defined.
I have next structure
gruntfile.js
grunt/tasks/functions.js
grunt/tasks/styles.js
The content of the files is as follows:
gruntfile.js
module.exports = function(grunt) {
require('jit-grunt')(grunt)
function globalFunctionOne(param1) {
console.log('yay it works from main file');
}
grunt.initConfig({});
console.log(grunt.config());
grunt.loadTasks('grunt/tasks')
grunt.registerTask('default', ['sass:dist'];
}
functions.js
module.exports = function(grunt) {
function globalFunctionTwo(param1) {
console.log('yay it works from partial file');
}
}
styles.js
module.exports = function(grunt) {
grunt.config('sass', {
options: {
implementation: sass,
includePaths: globalFunctionOne('dev'),
outputStyle: 'expanded',
sourceMap: true
},
dist: {
files: {
globalFunctionTwo('dist'),
}
}
});
}
2,000 lines? That's a long gruntfile. Here's a gruntfile you can adopt to break apart your code into something more modular:
gruntfile.js
function init(grunt) {
"use strict";
function loadConfig(pattern) {
let config = {},
fileName,
fileData;
grunt.file.expand(pattern).forEach(function (filePath) {
fileName = filePath.split('/').pop().split('.')[0];
fileData = require('./' + filePath)(grunt);
config[fileName] = fileData;
});
return config;
}
function loadGrunt() {
const config = {
pkg: grunt.file.readJSON('package.json')
};
require('load-grunt-tasks')(grunt);
if (grunt.file.exists('grunt/tasks')) {
grunt.log.writeln('task directory found, loading tasks...');
grunt.loadTasks('grunt/tasks');
}
grunt.util._.extend(config, loadConfig('grunt/configs/**/*.js'));
grunt.initConfig(config);
}
loadGrunt();
}
module.exports = init;
What this gruntfile does:
It dynamically creates a config object for grunt to use by loading
every .js file it finds in project-root/grunt/configs. The name
of each file corresponds to its key for the grunt config object.
It dynamically loads any tasks from project-root/grunt/tasks
If your config uses grunt-contrib-copy, then in your project, you'd have a config file resembling the following:
project-root/grunt/configs/copy.js
module.exports = function (grunt) {
"use strict";
return {
base: {
options: {
process: function (content) {
return grunt.template.process(content);
}
},
src: 'grunt/templates/base.url.js',
dest: 'www/source/config/base.url.js'
},
pluginManifest: {
src: 'cordova/template/package.json',
dest: '<%= grunt.task.current.args[0] %>/package.json'
},
splashScreens: {
expand: true,
cwd:"grunt/templates/",
src: "screen/**",
dest: '<%= create.dest %>/res/'
}
};
};
You can then shift your global functions to a helpers javascript file and import them into the configs in typical Node.js fashion:
project-root/grunt/configs/sass.js
module.exports = function (grunt) {
const helpers = require("../helpers.js)(grunt);
return {
options: {
implementation: sass,
includePaths: helpers.globalFunctionOne('dev'),
outputStyle: 'expanded',
sourceMap: true
},
dist: {
files: {
globalFunctionTwo('dist'),
}
}
};
};
project-root/grunt/helpers.js
module.exports = function (grunt) {
function globalFunctionOne(param1) {
console.log('yay it works from main file');
}
function globalFunctionTwo(param1) {
console.log('yay it works from partial file');
}
return {
globalFunctionOne,
globalFunctionTwo
};
};

PDFMAKE: 'Roboto-Regular.ttf' not found in virtual file system ONLY AFTER GULP

I created a simple app using knockout/bootstrap/gulp that downloads a pdf using pdfMake.js. It works fine in debug mode using VS2017. After publishing and using gulp it gives this error when run: File 'Roboto-Regular.ttf' not found in virtual file system
Note: After gulp, all JS files are in one script.js file.
I tried many things, it always works when debugging, as soon as I run gulp, it gives the error.
I tried joepal1976's solution from here (what I did with the dependencies in require.config.js)
Someone suggested .pipe(uglify({
compress: {
hoist_funs: false
}
})) which doesn't appear to help.
Included in require.config like so:
var require = {
baseUrl: ".",
paths: {
"jquery": "js-libs/jquery.min",
"bootstrap": "js-libs/bootstrap.min",
"crossroads": "js-libs/crossroads.min",
"hasher": "js-libs/hasher.min",
"knockout": "js-libs/knockout",
"knockout-projections": "js-libs/knockout-projections.min",
"signals": "js-libs/signals.min",
"text": "js-libs/text",
"vfs_fonts": "js-libs/vfs_fonts",
"pdfMake": "js-libs/pdfmake.min"
},
shim: {
"bootstrap": { deps: ["jquery"] },
'pdfMake':
{
exports: 'vfs_fonts'
},
'vfs_fonts':
{
deps: ['pdfMake'],
exports: 'vfs_fonts'
}
}
};
JS for the page:
define(["knockout", "text!./home.html"], function (ko, homeTemplate) {
function HomeViewModel(route) {
var thisVM = this;
this.VMInit = function () {
var thePDF = {
content: [
'My test invoice.',
]
};
pdfMake.createPdf(thePDF).download('pdf_test.pdf');
}
thisVM.VMInit();
}
return { viewModel: HomeViewModel, template: homeTemplate };
});
The Gulp file:
//-----------------------------------------------------------------------
// Node modules
var fs = require('fs'),
vm = require('vm'),
merge = require('deeply'),
chalk = require('chalk'),
es = require('event-stream');
//-----------------------------------------------------------------------
// Gulp and plugins
var gulp = require('gulp'),
rjs = require('gulp-requirejs-bundler'),
concat = require('gulp-concat'),
clean = require('gulp-clean'),
replace = require('gulp-replace'),
uglify = require('gulp-uglify'),
htmlreplace = require('gulp-html-replace');
// Config
var requireJsRuntimeConfig =
vm.runInNewContext(fs.readFileSync('src/app/require.config.js') + '; require;');
requireJsOptimizerConfig = merge(requireJsRuntimeConfig, {
out: 'scripts.js',
baseUrl: './src',
name: 'app/startup',
paths: {
requireLib: 'js-libs/require'
},
include: [
'requireLib',
'components/nav-bar/nav-bar',
'components/home-page/home',
'text!components/about-page/about.html'
],
insertRequire: ['app/startup'],
bundles: {
// If you want parts of the site to load on demand, remove them from the 'include' list
// above, and group them into bundles here.
// 'bundle-name': [ 'some/module', 'another/module' ],
// 'another-bundle-name': [ 'yet-another-module' ]
}
});
//-----------------------------------------------------------------------
// Discovers all AMD dependencies, concatenates together all required .js
files, minifies them
gulp.task('js', function () {
return rjs(requireJsOptimizerConfig)
.pipe(replace('Views/src/', ''))
.pipe(replace('img/', 'Assets/img/'))
.pipe(replace('css/', 'Assets/css/'))
.pipe(uglify({
preserveComments: 'some'
}))
.pipe(gulp.dest('./dist-app/Assets/js/'));
});
gulp.task('css', function () {
return gulp.src(['./src/css/bootstrap.css',
'./src/css/bootstrap-switch.css',
'./src/css/dataTables.bootstrap.css',
'./src/css/dataTables.colVis.css',
'./src/css/dataTables.responsive.css',
'./src/css/daterangePicker.css'])
.pipe(concat('styles.css'))
.pipe(gulp.dest('./dist-app/Assets/css/'));
});
// Copies index.html, replacing <script> and <link> tags to reference production
URLs
gulp.task('html', function () {
return gulp.src('./src/index.html')
.pipe(htmlreplace({
dependencies_top: '<link href="Assets/css/styles.css"
rel="stylesheet">',
dependencies_bottom: '<script src="Assets/js/scripts.js"></script>'
}))
.pipe(gulp.dest('./dist-app/'));
});
// Removes all files from ./dist/
gulp.task('clean', function () {
console.log("the clean task");
return gulp.src('./dist-app/**/*', { read: false })
.pipe(clean());
});
// All tasks in [] must complete before 'default' can begin
gulp.task('default', ['html', 'js', 'css'], function (callback) {
callback();
console.log('\nPlaced optimized files in ' + chalk.magenta('dist-app/\n'));
});
The Startup.js file if its helpful:
define(['jquery',
'knockout',
'./router',
'bootstrap',
'knockout-projections',
'pdfMake',
'vfs_fonts'], function ($, ko, router) {
// Components can be packaged as AMD modules, such as the following:
ko.components.register('nav-bar', { require: 'components/nav-bar/nav-bar' });
ko.components.register('home-page', { require: 'components/home-page/home'
});
// ... or for template-only components, you can just point to a .html file
directly:
ko.components.register('about-page', {
template: { require: 'text!components/about-page/about.html' }
});
ko.components.register('new-page', { require: 'components/new-page/new-page'
});
// [Scaffolded component registrations will be inserted here. To retain this
//feature, don't remove this comment.]
// Start the application
ko.applyBindings({ route: router.currentRoute });
});
Following code worked for me:
import pdfMake from "pdfmake/build/pdfmake";
import pdfFonts from "pdfmake/build/vfs_fonts";
pdfMake.vfs = pdfFonts.pdfMake.vfs;
I battled with this recently on stackblitz when using it with angular. the issue was pdfmake.vfs on the window object was not being set. so i had to manually set it in the constructor of my pdf service like so.
constructor() {
(window as any).pdfMake.vfs = pdfFonts.pdfMake.vfs;
}
I came across this issue and resolved it by including vfs_fonts.js just after the pdfmake Javascript file.
Here is my code, you should just need to set the file path to wherever your copy of the file is placed.
<script src="~/Content/DataTables/pdfmake-0.1.32/pdfmake.min.js"></script>
<script src="~/Content/DataTables/pdfmake-0.1.32/vfs_fonts.js"></script>
CDN LINK
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.53/pdfmake.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.32/vfs_fonts.js"></script>
please follow the hierarchy/dependency of links else it won't work
It is just the sequence of the files, add first the pdfmake and then vfs_fonts.
#Rijo solution worked in one file, but oddly enough refused to work in another file.
In the other file I had to use:
import pdfMake from "pdfmake/build/pdfmake";
import pdfFonts from "pdfmake/build/vfs_fonts";
// Wherever you call createPdf, you have to pass VFS
pdfMake.createPdf(docDefinition, null, null, pdfFonts.pdfMake.vfs).open();

Custom grunt task to concat multiple JS files from different folders into a single JS file

I am writing custom grunt task to concat multiple JS files from different folders into a single JS file. When i run grunt command, it shows that "Running 'read-folder-concat' task. After that, nothing happens. I am unable to see any errors on the console. I am totally stuck here.
There are two files, one is Gruntfile.js & concat-task.js. I am sharing code here of these files.
Gruntfile.js
// Grunt configuration file
(function () {
'use strict';
module.exports = function (grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: ';'
}
}
});
//load grunt tasks
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.task.loadTasks('./tasks');
//register grunt default task
grunt.registerTask('default', ['read-folder-concat']);
};
})();
concat-task.js
(function () {
'use strict';
module.exports = function (grunt) {
function readFolderAndConcat(pathArr) {
// get the current concat config
var i,
src = [],
concat = grunt.config.get('concat') || {};
for (i = 0; i < pathArr.length; i++) {
grunt.file.expand(pathArr[i]).forEach(function (dir) {
src.push(dir);
});
}
// set the config for this modulename-directory
concat = {
dist: {
src: [src.join(",")],
dest: 'app_min_safe/app/min/app.js'
}
};
// save the new concat configuration
grunt.config.set('concat', concat);
// when finished run the concatinations
grunt.task.run('concat');
}
grunt.registerTask("read-folder-concat", "read folder dynamically and concat it.", function () {
readFolderAndConcat(["app/*.js", "app/modules/js/*/*.js"]);
});
};
})();
Please help me understand what is the issue in my concat task.

how to copy a single file in each directory

I would like to configure and execute a task that copies a specified file in each dir recursively. In specific I would like to have "index.php" copied in each directory and subdirectory of my web project, if that doesn't exist. I've tried to use multidest and copy but multidestdoesn't seem to allow to specify paths by means of a wildcard (too bad!). How could I solve this? Thank you
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
multidest: {
tst: {
tasks: ['copy:indexphp'],
dest: '**/' //THIS DOESN'T WORK, I SHOULD SPECIFY ANY DIR/SUBDIR/ETC..
},
},
copy: {
indexphp: {
expand: true,
cwd: 'classes',
src: 'index.php',
filter: function (filepath) {
// NPM load file path module.
var path = require('path');
// Construct the destination file path.
var dest = path.join(
grunt.task.current.data.dest,
path.basename(filepath)
);
// Return false if the file exists.
return !(grunt.file.exists(dest));
}
},
[...]

grunt.registerTask can not modify global grunt task settings

The following code reads the contents of every subdirectory js inside of app/modules/ (eg. app/modules/module1/js/, app/modules/module2/js/, aso.)
this script worked before WITHOUT using the last command grunt.task.run('concat:' + dir);.
for a while now it stopped working so that i had to add a call to the task concat inside of the forEach loop.
normally I would have saved the new configuration inside of the concat configuration and call sometime later the resulting concat task.
grunt.registerTask('preparemodulejs', 'iterates over all module directories and compiles modules js files', function() {
// read all subdirectories from your modules folder
grunt.file.expand('./app/modules/*').forEach(function(dir){
// get the current concat config
var concat = grunt.config.get('concat') || {};
// set the config for this modulename-directory
concat[dir] = {
src: [dir + '/js/*.js', '!' + dir + '/js/compiled.js'],
dest: dir + '/js/compiled.js'
};
// save the new concat config
grunt.config.set('concat', concat);
grunt.task.run('concat:' + dir); // this line is new
});
});
what exactly changed in recent versions that i have to add an explicit task.run line?
and is there any way to write the settings of this task into the settings of an existing concat task so that if i have other manual additions to that configuration those won't run for each directory scanned?
thanks for help.
grunt.task.run(); despite it's name, does not run tasks. Grunt is always synchronous so grunt.task.run() will queue tasks to run after the current task has finished.
So I would avoid using grunt.task.run() within an array but rather build a list of tasks/targets to run afterward:
grunt.registerTask('preparemodulejs', 'iterates over all module directories and compiles modules js files', function() {
var tasks = [];
// read all subdirectories from your modules folder
grunt.file.expand('./app/modules/*').forEach(function(dir){
// get the current concat config
var concat = grunt.config.get('concat') || {};
// set the config for this modulename-directory
concat[dir] = {
src: [dir + '/js/*.js', '!' + dir + '/js/compiled.js'],
dest: dir + '/js/compiled.js'
};
// save the new concat config
grunt.config.set('concat', concat);
tasks.push('concat:' + dir);
});
// queues the tasks and run when this current task is done
grunt.task.run(tasks);
});
We can also provide a config directly here for different tasks to run on the go for bigger projects having multiple modules. Even if the we need to process files outside of the root directory:
grunt.registerTask('publishapp', 'uglify ivapp.js and upload to server', function (){
var tasks = [];
grunt.file.expand('../outerdirectory/').forEach(function(dir) {
// config for uglify that needs to execute before uploading on server
var uglify = {
options: {
compress: {
drop_console: true,
},
banner: '/* Banner you want to put above js minified code. */\n'
},
all: {
files: [{
expand: true,
cwd: '../',
src: ['somedir/some.js'],
dest: 'build',
ext: '.js',
extDot: 'last'
}]
}
};
// set grunt config : uglify
grunt.config.set('uglify', uglify);
});
// prepare a tasks list
tasks.push('uglify:all');
tasks.push('exec:publish');
// execute a tasks to perform
grunt.task.run(tasks);
});

Categories

Resources