grunt-init copyAndProcess corrupting png files - gruntjs

I've just started using grunt-init. I have everything working, except I'm finding that any images/*.png files in my template get corrupted in transit to the destination folder.
I suspect that the init.copyAndProcess function is corrupting them (they open in Gimp from the template folder but not the destination folder).
How can I do a copy instead of copyAndProcess for a subset of the files in my template? Preferably using a pattern like 'images/**' to identify the files.

You actually can use filesToCopy, since you pass a noprocess hash, telling to grunt-init the files it should not process.
Refer to http://gruntjs.com/project-scaffolding#copying-files (grunt-init documentation)
and an example at line 73 of this commit https://github.com/gruntjs/grunt-init-jquery/blob/ecf070d7469d610441458111bc05cd543ee5bbc0/template.js.

Well, I would have preferred something more concise, similar to what was already in the template.js:
var files = init.filesToCopy( props );
init.copyAndProcess( files, props );
But this works:
var src_path = init.srcpath( 'images/' );
var dest_path = init.destpath( ) + '/images/';
// Copy the images folder this way to prevent corrupting the files
grunt.file.recurse( src_path, function( abspath, rootdir, subdir, filename ) {
if ( subdir == undefined ) {
var dest = dest_path + filename;
} else {
var dest = dest_path + subdir + '/' + filename;
}
grunt.file.copy( abspath, dest );
});

Related

Replace Google Fonts with self hosted fonts

I'm currently porting parts of a legacy codebase which has more than 100 themes that each come with their own css files. Those files are full with hardcoded links to Google Fonts which need to be replaced due to GDPR.
Is there some kind of automated tool available which scans through these files, replaces the link to Google Fonts and downloads all the assets? I've found a couple of semi-automated tools online but they all require copy & paste and manual download of the files. That's okay for 2-3 fonts but not for hundreds of them. Any tips for that?
I have put some efforts to create this NodeJS script.
This script searches for all css files and extracts the woff font url. Then, it replaces it with absolute path of the downloaded file against the url it found, also downloads the file in the relevant directory which can be clearly identified in the snippet as specified with fontDownloadDirectoryPath variable.
This script can be modified and improved further but as of now, provides the required functionality at its base level.
I hope this can serve as a starting point atleast to solve the stated problem or can be used completely as a solution changes few variables, given that my assumptions of few required things to get to this solution are correct.
Please feel free to modify, accordingly like the regex pattern to match something else, adding few other font types in the pattern, adding few more code to make it more robust and generalised, etc for other possibilities.
const path = require('path');
const fs = require('fs');
const https = require("https");
// update assets/css with your css path
const directoryPath = path.join(__dirname, 'assets/css');
let fontDownloadDirectoryPath = path.join(__dirname, 'assets/fonts')
let fontDownloadDirectoryFileFullPath = path.join(__dirname, 'assets/fonts/fontsDownloadUrlList.json')
fs.readdir(directoryPath, function (err, files) {
//handling error
if (err) {
return console.log('Unable to scan directory: ' + err);
}
//listing all files using forEach
files.forEach(function (file) {
// Do whatever you want to do with the file
let file_full_path = directoryPath + "/" + file
fs.readFile(file_full_path, 'utf8', (err, content) => {
if (err) {
console.error(err);
return;
}
// console.log(content);// show the content of readed file
let found = content.match(/url\(['"]([^"']+(woff2|eot|woff|ttf)["'])+\)/gi)
console.log(file_full_path, found);
let updated_content = content
if (found) {
if (fs.existsSync(fontDownloadDirectoryFileFullPath)) {
// enter the code to execute after the folder is there.
console.log('file exists')
fs.readFile(fontDownloadDirectoryFileFullPath, 'utf8', (err, read_content) => {
let read_content_json = JSON.parse(read_content)
read_content_json.push(...found)
fs.writeFile(fontDownloadDirectoryFileFullPath, JSON.stringify(read_content_json), function () { })
})
} else {
fs.writeFile(fontDownloadDirectoryFileFullPath, JSON.stringify(found), function () { })
}
console.log(found)
found.forEach((item) => {
let fontFileUrl = item.split("'")[1]
let fontFileName = fontFileUrl.split("/")[fontFileUrl.split("/").length - 1]
console.log(fontFileUrl, fontFileName)
https.get(fontFileUrl, response => {
var body = '';
var i = 0;
response.on('data', function (chunk) {
i++;
body += chunk;
console.log('BODY Part: ' + i);
});
response.on('end', function () {
console.log(body);
fs.writeFileSync(fontDownloadDirectoryPath + "/" + fontFileName, body, { encoding: 'utf8', flag: 'w' }, (err) => { console.log(err) })
console.log('Finished');
});
});
updated_content = updated_content.replace(item, "url('" + fontDownloadDirectoryPath + "/" + fontFileName + "')")
})
} else {
updated_content = content;
}
fs.writeFileSync(file_full_path, updated_content, { encoding: 'utf8', flag: 'w' })
});
});
});
I used below css file in root/assets/css directory with styles.css name for testing the above script:
#font-face {
font-family: 'BR Firma';
src: url('https://fonts.gstatic.com/s/opensans/v29/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsiH0B4taVQUwaEQbjB_mQ.woff') format('woff');
font-weight: bold;
font-style: normal;
font-display: swap;
}
without having more background on the project, directory structure, and so on, I will outline how the task could be done as of now:
Scan all the directories or URLs of the project (if you run it locally or remotely) if the fonts are not being imported from one main CSS file (could happen).
Get all the Google Fonts URLs
Download all the assets (i.e. fonts from the links, maybe some pics also, etc.)
So, although you can totally do this locally with directories, here I will explain a way to do it with the browser for brevity - and possibly convenience - with Python. I am assuming you have access to the project's URLs, ofc.
You can follow this approach to scrape the URLs you want. Pass it a list from the sitemap to go through all the URLs in sequence. Then you can filter the list you get to account only for Google Fonts, simply add the in operator (as in here) to get true or false, respectively.
substring = 'https://fonts.googleapis.com'
if substring in element:
list.append(element)
else:
#do nothing, don't add it to list
Now you should have all the URLs you are interested in. If the project has several HTML pages with different fonts, those are the ones you need to scan - or maybe just all of them to be sure. Note: it is usually useful to store the list in a file to further add this code to the previous script. More info.
with open('urls.txt', 'w') as f:
f.write(element)
To download the assets, you can use this approach. Since you have all the URLs, you should be able to.
And that's pretty much it! If you add more insight into the project structure we could complete the scripts more precisely. Also, you can quickly use Jupyter Notebook to run the scripts as you tune them. In the meantime, the open details to clarify would be:
Scan websites or files?
Only HTML files or all the projects?
What to download? The font assets only?
Python script works fine for this task?
Any IDE can do, just "search and replace in files", with the appropriate patterns.
For example: PHPStorm: Find and replace text using regular expressions. Finding all the occurrences alone is already worth something and an IDE might help with "porting parts of a legacy codebase".

gulp sass: merge folders with file inheritance

I have a folder structure like this (simplified):
|-project1
|--_file1.scss
|--_file2.scss
|--_file3.scss
|
|-project2
|--_file2.scss
|
|-css
|--project1.css
|--project2.css
I am looking for a way to compile the sass files with inheritance. The idea behind this is, that I have a base project (project1) and project 2 only contains those files that need to be changed.
So upon compilation gulp should render 2 css files:
project1.css
This contains only the files from project1/scss/ folder
project2.css
This one should contain file1 and file3 from project 1 and file2 from project 2.
Is this possible? What modules would be needed?
Thank you
Here is something that should work for you. I note that you have all and only partials in your project folders, i.e. _file1.scss, _file2.scss, etc. You will have to have at least one file that is not a partial that imports those partials for sass to work.
const gulp = require('gulp');
const fs = require('fs');
const path = require('path');
const filter = require('gulp-filter');
const sass = require('gulp-sass');
const concat = require('gulp-concat');
const addsrc = require('gulp-add-src');
// const glob = require("glob");
const sources = ['project1', 'project2', 'project3'];
// could glob your sourceFolders with something like
// const sources = glob.sync("project*");
const filterSources = Object.keys(sources);
function isUnique(file, index) {
console.log(path.basename(file.path)); // file1.scss, file2.scss, etc. all from project1
baseName = path.basename(file.path);
folder = sources[index]; // project
// does that basename exist in thecurrent project (sources[index] )
return !fs.existsSync(folder + path.sep + baseName);
}
gulp.task('default', function () {
// loop through all the project folders
filterSources.forEach(function (project, index) {
const f = filter(function (file) {
return isUnique(file, index);
});
// always using project1 files as basis
const stream = gulp.src('./project1/*.scss')
// filter out from the source stream (project1 files) any files that appear in the current project directory
.pipe(f)
// add all files from the current project directory, i.e., project2, project3, etc.
.pipe(addsrc.append('./' + sources[index] + '/*.scss'))
.pipe(sass().on('error', sass.logError))
// give the concat the filename of the current project
.pipe(concat(sources[index] + '.css'))
.pipe(gulp.dest('css'));
return stream;
});
This works for any number of project folders, just make sure that your non-partial scss file that imports the others is not one that is filtered out.
But it looks like you don't really want any partials anyway since you want to concat all files in each project so remove those leading underscores from each file name.

How to limit file uploads to Amazon S3 with Slingshot.createDirective

Am trying to limit my uploads to amazon s3 to 10 pictures, i came across this link
https://github.com/themeteorchef/uploading-files-to-amazon-s3/blob/master/code/server/slingshot.js
for some reason it doesn't work with me
this is my code
Slingshot.createDirective( "uploadToAmazonS3Cg2", Slingshot.S3Storage, {
bucket: "bucket-name",
region: 'ap-southeast-1',
acl: "public-read",
authorize: function () {
return true;
},
key: function ( file ) {
var user = Meteor.users.findOne( this.userId );
return user.emails[0].address + "/screenshots" + "/" + file.name;
}
});
here is my uploader in the html file
Application ScreenShots:
{{> uploader config="2"}}
and this is how am calling my slingshot method based on the link i attached above
var uploader
if (config === '1') {
uploader = new Slingshot.Upload( "uploadToAmazonS3Cg1" );
}
if (config === '2') {
uploader = new Slingshot.Upload( "uploadToAmazonS3Cg2" );
} else {
uploader = new Slingshot.Upload( "uploadToAmazonS3Cg3" );
}
i returned true, from my understanding, it should allow me to upload as many as i want but i only can upload one file.
am i missing something here? are there any alternatives to set the limitation?
Taking a look at the source-code for slingshot, you need to upload the files one at a time. To limit the number of files uploaded in total by a user, you'd need to use the authorize function and store how many files they've uploaded in Mongo. I'm not sure if you mean 10 files total or 10 at a time based on your question, but if it was 10 files at a time you'd simply validate that using jQuery validation on your file input element.

How to pass options to handlebars.precompile?

So I've got this code that is running fine (I'm using gulp not grunt for what it matters) :
var handlebars = require('handlebars'),
rename = require('gulp-rename'),
map = require('vinyl-map');
gulp.task('test', function(){
return gulp.src(config.path.template+"/*.handlebars")
.pipe(map(function(contents) {
return handlebars.precompile(contents.toString());
}))
.pipe(rename({ extname: '.js' }))
.pipe(gulp.dest(config.path.template+"/test"))
});
Everything runs perfectly, the .js files generate in the good folder, but I need them to generate without the -s parameter. For an example, when I run handlebars path/to/my/hbs.handlebars -f path/to/my/out/folder.js -s (or --simple), the file generated is the same. But I need this command to run without the -s parameter, and I can't find a way to pass this argument in my gulpfile. I tried alot of things, in a String, in a Json, in an array, tried to go with -s false, with simple false, with isSimple false (something I found in handlebars code).
None of this is working and I really need to pass the -s parameter to false. I assume that I need to do something like :
[...]
return handlebars.precompile(contents.toString(), options);
[...]
But I can't find the proper syntax or way to use these options. And that is my problem.
PS : I use this instead of gulp-handlebars so that I can use the version of handlebars I want to use and not another.
EDIT
Searching in handlebars.js code, I just found that options is an object, but I can't find what he's filled with as I'm not a good javascript user.
It seems to me there's no such switch in the source.
Taken from the precompiler source used from the command line tool:
if (opts.simple) {
output.push(Handlebars.precompile(data, options) + '\n');
} else if (opts.partial) {
if (opts.amd && (opts.templates.length == 1 && !fs.statSync(opts.templates[0]).isDirectory())) {
output.push('return ');
}
output.push('Handlebars.partials[\'' + template + '\'] = template(' + Handlebars.precompile(data, options) + ');\n');
} else {
if (opts.amd && (opts.templates.length == 1 && !fs.statSync(opts.templates[0]).isDirectory())) {
output.push('return ');
}
output.push('templates[\'' + template + '\'] = template(' + Handlebars.precompile(data, options) + ');\n');
}
opts is what you passed in, another variable options is passed to precompile. The decoration is added by the command line tool.
There's a second block with closing brackets a few lines below in the script.
You best copy that source to your code or maybe access the .cli object in your gulp script.

How do I process multiple files as templates with yeoman generator?

I'm working on a custom generator that I originally wrote with grunt-init. One difference I'm noticing is grunt-init automatically processes all the files in root as templates but with yeoman generator you have to do this using .template(). I'm familiar with how to process any individual file with .template() but is it possible to process an entire directory?
This issue is an annoying one. I came across this when I used yeoman for the first time. I think the below code snippet can help you.
this.directory('scripts', 'scripts');// script is folder name
Looking at the Yeoman generator code, there doesn't seem to be a built in way to do this. The way I solved this was to copy some of the code from the built in generator code and modify it for my needs. I determine if a file is a template based on the _ prefix convention since I want to rename the files to exclude that prefix, but you could just treat every file as a template and it would work fine. This will copy all of the files in the directory, so what I also did is to exclude the .DS_STORE files that you find by default on OSX, but since that is a specific case I didn't include that here.
require('path');
MyGenerator.prototype._processDirectory = function(source, destination) {
var root = this.isPathAbsolute(source) ? source : path.join(this.sourceRoot(), source);
var files = this.expandFiles('**', { dot: true, cwd: root });
for (var i = 0; i < files.length; i++) {
var f = files[i];
var src = path.join(root, f);
if(path.basename(f).indexOf('_') == 0){
var dest = path.join(destination, path.dirname(f), path.basename(f).replace(/^_/, ''));
this.template(src, dest);
}
else{
var dest = path.join(destination, f);
this.copy(src, dest);
}
}
};
Yeoman uses mem-fs-editor, which has support for glob patterns. However the documentation is not very clear, and you may miss that point. Here is the documentation of copyTpl, that says it accepts the same options as copy. So since copy has suppport for glob patterns, copyTpl too.
At any point on your yeoman generator you can do:
const from = 'myFolder/**.js'
const to = 'project/'
this
.fs
.copyTpl(
this.templatePath(from),
this.destinationPath(to),
this.props, {interpolate: /<%=([\s\S]+?)%>/g}
);
},
Not that, if you are using a glob pattern the destination path should be a folder.

Resources