Load an external file in a grunt-contrib-jade task - gruntjs

So I've got this grunt-contrib-jade setup that is working just fine
Everything is fine when I include my data in the (exported) Gruntfile myself:
module.exports = {
website: {
options: {
data: {
pageTitle: "This is my website",
greeting: "Hello world",
},
debug: true,
pretty: true
},
files: {
'build/website/index.html': 'src/jade/template/index.jade'
}
}
};
It merges my index.jade with my data values and my index.html output is the way it should be. But when I want to load an external file to define my data it goes wrong:
options: {
data: function (dest, src) {
// Return an object of data to pass to templates
return require('src/jade/template/locals.json');
},
debug: true,
pretty: true
},
files: {
'build/website/index.html': 'src/jade/template/index.jade'
}
The require path is valid, I triple checked it. It's located in the same folder as my index.jade. However I still keep getting this error:
Running "jade:website" (jade) task
>> Jade failed to compile "src/jade/template/index.jade".
>> Error: Cannot find module './locals.json'
>> Destination not written because compiled files were empty.
I tried just about everything, but I just don't see it.

Local modules need to be prepended with './' when you require them.
data: function (dest, src) {
// Return an object of data to pass to templates
return require('./src/jade/template/locals.json');
}
will work. You're not doing anything with the function (at least yet), so this could also be
data: require('./src/jade/template/locals.json')
or even
data: grunt.file.readJSON('./src/jade/template/locals.json').

Related

How to set multiple output when build lib with vite

When I try to build a lib in vue3, I want to set multiple output file. Code like this:
rollupOptions {
output: [
{
file: 'bundle.js',
format: 'cjs'
},
{
file: 'bundle.min.js',
format: 'iife',
name: 'version',
}
]
}
Then I will get an error:
You must set either "output.file" for a single-file build or "output.dir" when generating multiple chunks"
So, how should I do can make this work?
vite: 2.3.7
👉 You are missing the input config in rollupOptions.
The following full vite config example will generate a index.bundle.[moduleFormat].js
(you may need to adjust file path according to your setup)* :
import { defineConfig } from "vite";
// https://vitejs.dev/config/
export default (opts: { mode: "production" | "development"; command: "build" | "serve" }) => {
return defineConfig({
build: {
target: "es2020",
commonjsOptions: {
sourceMap: false
},
rollupOptions: {
input: {
index: "./src/index.js"
},
/* single
output: {
format: "umd",
strict: false,
chunkFileNames: `[name].[hash].js`,
entryFileNames: "[name].bundle.umd.js",
dir: "dist"
},
*/
// array config example
// from rollup: export type InternalModuleFormat = 'amd' | 'cjs' | 'es' | 'iife' | 'system' | 'umd';
output: [
{
format: 'cjs',
entryFileNames: "[name].bundle.[format].js",
},
{
format: 'es',
entryFileNames: "[name].bundle.[format].js",
},
{
format: 'umd',
entryFileNames: "[name].bundle.[format].js",
},
]
}
}
});
};
💡 To understand this answer better, please read the following sentence :
If you provide an array of entry points or an object mapping names to entry points, they will be bundled to separate output chunks.
And unless the output.file option is used, generated chunk names will follow the output.entryFileNames option. When using the object form, the [name] portion of the file name will be the name of the object property while for the array form, it will be the file name of the entry point.
Note that it is possible when using the object form to put entry points into different sub-folders by adding a / to the name.
If we follow the doc we can get more precisions and we know that :
output.dir
Type: string
Set in build.rollupOptions
Is for: The directory in which all generated chunks are placed and this option is required if more than one chunk is generated. Otherwise, the file option can be used instead.
output.file
Type: string
The file to write to. Can only be used if not more than one chunk is generated.
I guess your looking for https://vitejs.dev/guide/build.html#library-mode
And see https://vitejs.dev/config/#build-lib to tweak your needs.

grunt-contrib-copy syntax for process option confusion

I'm trying to replace some placeholders in different files as I copy. My gruntfile works fine, but adding in the process option to do the replacements, it's just not working. Below is the relevant section of my gruntfile:
grunt.initConfig({
copy: {
js: {
files: [{
expand: true,
cwd: 'src/wp-content/themes/pilau-starter/',
src: ['**/*.js'],
dest: 'public/wp-content/themes/pilau-starter/'
}],
options: {
process: function ( content ) {
console.log( content );
content = content.replace( /pilauBreakpointLarge/g, breakpoints.large );
content = content.replace( /pilauBreakpointMedium/g, breakpoints.medium );
return content;
}
}
},
}
});
The paths can be understood in the context of the code on GitHub: https://github.com/pilau/starter (the public directory isn't committed to the repo because it's a starter theme). Those paths are variables in my original Gruntfile, and are working fine in all other tasks.
All the vars are set up OK. I've included the console.log( content ) to check if the process function's actually running - it doesn't seem to be, so I guess it's basic syntax.
There's an answer (https://stackoverflow.com/a/28600474/1087660) which seems to address this, but as far as I can tell, that way of doing it is simply bad JS syntax - not sure how it got marked as right.
--verbose output for running the copy task:
Running "copy:js" (copy) task
Verifying property copy.js exists in config...OK
Files: src/wp-content/themes/pilau-starter/js/admin.js -> public/wp-content/themes/pilau-starter/js/admin.js
Files: src/wp-content/themes/pilau-starter/js/flickity.js -> public/wp-content/themes/pilau-starter/js/flickity.js
Files: src/wp-content/themes/pilau-starter/js/global.js -> public/wp-content/themes/pilau-starter/js/global.js
Files: src/wp-content/themes/pilau-starter/js/modernizr.js -> public/wp-content/themes/pilau-starter/js/modernizr.js
Files: src/wp-content/themes/pilau-starter/js/picturefill.js -> public/wp-content/themes/pilau-starter/js/picturefill.js
Files: src/wp-content/themes/pilau-starter/js/respond.js -> public/wp-content/themes/pilau-starter/js/respond.js
Options: processContent=false, processContentExclude=[], process=undefined
Options: processContent=false, processContentExclude=[], process=undefined
Copying src/wp-content/themes/pilau-starter/js/admin.js -> public/wp-content/themes/pilau-starter/js/admin.js
Reading src/wp-content/themes/pilau-starter/js/admin.js...OK
Writing public/wp-content/themes/pilau-starter/js/admin.js...OK
Your version of grunt-contrib-copy is 0.4.0. As correctly point out by #nemesv above the property name to use in this version would be processContent not process.
I cloned your repo and switched to json-breakpoints branch. And ran grunt copy:js and it replaced the content.
Now,when you run grunt copy:js --verbose it will still show this
processContent is logged undefined because grunt uses JSON.stringify to log a value. And JSON.stringify returns undefined when you pass it a function definition.
If you are interested, here's the method reponsible for logging all the option
Log.prototype.writeflags = function(obj, prefix) {
var wordlist;
if (Array.isArray(obj)) {
wordlist = this.wordlist(obj);
} else if (typeof obj === 'object' && obj) {
wordlist = this.wordlist(Object.keys(obj).map(function(key) {
var val = obj[key];
return key + (val === true ? '' : '=' + JSON.stringify(val));
}));
}
this._writeln((prefix || 'Flags') + ': ' + (wordlist || '(none)'.cyan));
return this;
};
This doesn't appear to be an issue with the process option at all, but more an issue with srcThemeDir. I would log it to make sure you know exactly what it is, as it appears that it is causing the copy task to not find any files (and therefore not call the process function).

grunt-contrib-jade compiling to single JS with cwd

I am trying to compile multiple jade templates into single JS file using grunt-contrib-jade. Problem I'm facing is that with full path to templates, I get function names with full path. I want to avoid that, so I tried using cwd (without expand). This ended up with the following:
>> Source file "test.jade" not found.
>> Source file "test2.jade" not found.
Is there any way I could achieve what I plan? My grunt config for that task is as following:
jade: {
js: {
options: {
client: true,
amd: true
},
files: [ {
cwd: 'js/views/',
src: ['*.jade'],
dest: 'js/tmp/templates.js'
} ]
}
},
Thanks in advice,
Dracco
Silly me, didn't fully read the documentation of the plugin :(.
The solution is trivial, using the processName option:
options: {
client: true,
amd: true,
processName: function(path) {
var pathChunks = path.split('.')[0].split('/');
return pathChunks[pathChunks.length - 1];
}
}

Grunt convert markdown to pdf recursively and dynamically

I am stuck writing a Gruntfile which aim is to convert a bunch of Markdown files to PDF dynamically. Giving the current folder hierarchy:
root/
|_subfolder1
| |_filename1.md
|_subfolder2
|_filename2.md
...
|_node_modules
|_subfolderN
|filenameN.md
I would like to run a Markdown to PDF task which would process the md file and ouput a PDF file with the matching filename in the same output directory.
I did create a custom task which is parsing current directory, ignoring the mode_modules folder and get the markdown file, but I don't know how to configure the md2pdf task with the good properties to reflect dynamic folder mapping.
Here's my current Gruntfile:
module.exports = function(grunt) {
// 1 - Configuration
grunt.initConfig({
md2pdf: {
}
});
// 2 - Plugins
grunt.loadNpmTasks('grunt-md2pdf');
// 3 - Task registering
grunt.registerTask('default', 'Get Subfolders', function() {
grunt.file.recurse('.', callback);
function callback(abspath, rootdir, subdir, filename) {
var filenameOutExt;
// if current occurence is a file subdir == undefined
// checking subdir to true means it's not undefined and
// the current path is a directory
if(subdir) {
// excluding node_modules folder
if (!subdir.match('node_modules')) {
// only process markdown files
if(filename.match('.md')) {
filenameOutExt = filename.split('.')[0];
// now for each markdown files, run md2pdf task
// and ouput filenameOutExt.pdf in same folder
// as the input files
}
}
}
}
});
};
I am using this plugin: https://www.npmjs.com/package/grunt-md2pdf
So my questions is how should I configure the md2pdf task to pass it the markdown files and generate matching filename pdf output in same directory.
Output should be:
root/
|_subfolder1
| |_filename1.md
|_filename1.pdf
|_subfolder2
|_filename2.md
|_filename2.pdf
...
|_node_modules
|_subfolderN
|filenameN.md
|_filenameN.pdf
Thanks a lot
According to this line, this task uses the grunt.files utilitary function. This makes things easier for us!
First, it's not usual in grunt to create a different task to find the files you need in any other task.
That is, each task should receive the files it needs to operate on. For example...
coffee:
main:
files: [
expand: true
cwd: 'assets/script'
src: ['**/*.coffee']
dest: "assets/script"
ext: '.js'
]
(Note that this config is in a Gruntfile.coffee file, hence the CoffeeScript syntax)
This type of file configuration using glob expansion is one of the most common. You can find details in the documentation.
It's pretty obvious:
In every directory (**), take everything (/*) from assets/script/ that ends with .coffee. Put it into assets/script. Rename extensions to .js.
So, your task can probably be configured like that:
md2pdf: {
main: {
files: [ {
expand: true,
src: ['**/*.md', '!node_modules/**/*'],
dest: "pdf/"
} ]
}
}
Ok figured it out !
module.exports = function(grunt) {
// 2 - Plugins
grunt.loadNpmTasks('grunt-md2pdf');
grunt.registerTask('default', 'Dynamically generate PDF from MD', function() {
grunt.file.expand("./**/*.md").forEach( function(file) {
if(!file.match('./node_modules')) {
var md2pdf = grunt.config.get('md2pdf') || {};
md2pdf[file] = {
src: file,
dest: file + '.pdf'
};
grunt.config.set('md2pdf', md2pdf);
}
});
grunt.task.run('md2pdf');
});
};

How to get grunt.file.readJSON() to wait until file is generated by another task

I'm working on setting up series of grunt tasks that work with RequireJS r.js compiler:
1) generates a .json file listing of all files in a directory
2) strips the ".js" from the filename (requirejs requires this)
3) use grunt.file.readJSON() to parse that file and use as a configuration option in my requirejs compilation task.
Here is the relevant code from my gruntfile.js:
module.exports = function (grunt) {
grunt.initConfig({
// create automatic list of all js code modules for requirejs to build
fileslist: {
modules: {
dest: 'content/js/auto-modules.json',
includes: ['**/*.js', '!app.js', '!libs/*'],
base: 'content/js',
itemTemplate: '\t{' +
'\n\t\t"name": "<%= File %>",' +
'\n\t\t"exclude": ["main"]' +
'\n\t}',
itemSeparator: ',\n',
listTemplate: '[' +
'\n\t<%= items %>\n' +
'\n]'
}
},
// remove .js from filenames in module list
replace: {
nodotjs: {
src: ['content/js/auto-modules.json'],
overwrite: true,
replacements: [
{ from: ".js", to: "" }
]
}
},
// do the requirejs bundling & minification
requirejs: {
compile: {
options: {
appDir: 'content/js',
baseUrl: '.',
mainConfigFile: 'content/js/app.js',
dir: 'content/js-build',
modules: grunt.file.readJSON('content/js/auto-modules.json'),
paths: {
jquery: "empty:",
modernizr: "empty:"
},
generateSourceMaps: true,
optimize: "uglify2",
preserveLicenseComments: false,
//findNestedDependencies: true,
wrapShim: true
}
}
}
});
grunt.loadNpmTasks('grunt-fileslist');
grunt.loadNpmTasks('grunt-text-replace');
grunt.loadNpmTasks('grunt-contrib-requirejs');
grunt.registerTask('default', ['fileslist','replace', 'requirejs']);
I'm running into a problem where, if the "content/js/auto-modules.json" file doesn't already exist on load of my config file, the file.readJSON() is executed immediately, before the file exists and the entire task fails and throws "Error: Unable to read file " If the file already exists, everything works beautifully.
How can I set this up so that the task configuration waits for that file to be created in the first task, and modified in the second task before it tries to load & parse the JSON in it for the third task? Or is there another way (perhaps using a different plugin) to generate a json object in one task, and then pass that object to another task?
Old post but I had a similar experience.
I was trying to load a some json config like:
conf: grunt.file.readJSON('conf.json'),
but if this file did not exist then it would fall in a heap and not do anything.
So I did the following to load it and populate defaults if it didnt exist:
grunt.registerTask('checkConf', 'ensure conf.json is present', function(){
var conf = {};
try{
conf = grunt.file.readJSON('./conf.json');
} catch (e){
conf.foo = "";
conf.bar = "";
grunt.file.write("./conf.json", JSON.stringify(conf) );
}
grunt.config.set('conf', conf);
});
You still may have some timing issues but this approach may help someone with a readJSON error.

Resources