0

I've got this really simple Gruntfile that reads a plain text file line by line and is supposed to create an html file of the same name from each line (ie: eng_product_100x100.html) but it only works with one line in the file, not multiples.

This is the console response when trying to run with multiple lines:

Running tasks: createProducts

Running "createProducts" task
Writing product/eng/eng_product_100x100.html
eng_product_200x200.html
eng_product_300x300.html
eng_product_400x400.html...ERROR
Warning: Unable to write "product/eng/eng_product_100x100.html
eng_product_200x200.html
eng_product_300x300.html
eng_product_400x400.html" file (Error code: ENOENT). Use --force to continue.

Aborted due to warnings.

The Gruntfile:

module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json')
  });

  grunt.registerTask('createProducts', 'Create product banners', function() {
    this.async();

    var fs = require('fs');
    var fileName = 'filenames.xml';

    var str = fs.readFileSync(fileName, 'utf8');
    var arr = str.split('/n');
    for (i in arr){
      grunt.file.write(arr[i].split('_')[1]+'/'+arr[i].split('_')[0]+'/'+arr[i], 'test');
      console.log(arr[i]);
    }

  });

};
4t0m1c
  • 329
  • 1
  • 7
  • 20

1 Answers1

1

Your Gruntfile.js is almost correct. The reason why it is not working with multiple lines is due to the line of code reading:

var arr = str.split('/n');

Note the /n should be \r\n for a newline character. See here for the differences between DOS vs. Unix line endings That line of code should read:

var arr = str.split('\r\n');

Gruntfile.js

Here is the full corrected version of the Gruntfile.js, including some refactoring:

module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json')
    });

    grunt.registerTask('createProducts', 'Create product banners', function() {
        this.async();

        var fs = require('fs'),
            fileNameList = 'filenames.txt',
            str = fs.readFileSync(fileNameList, 'utf8'),
            arr = str.split('\r\n');

        for (var i in arr) {
            var newFilePath = arr[i].split('_')[1] + '/' + arr[i].split('_')[0] + '/' + arr[i] + '.html';
            grunt.file.write(newFilePath, '<html></html>');
            console.log('Created file: ' + newFilePath);
        }
    });

};

The notable changes after refactoring are:

  1. It reads the list from a filenames.txt rather than 'filenames.xml' (There's no need for the file to be .xml if it's just plain text as mentioned in your question.

  2. Added a new variable named newFilePath as it clearly indicates what all the arr[i].split() etc is really creating.

  3. Add '.html' to the newFilePath variable so the resultant files include a .html suffix.


UPDATE Updated answer to utilize \r\n to handle both DOS vs. Unix Line Endings.


UPDATE 2

Cross Platform Robustness

The above solution works successfully when filenames.txt is created on Windows. However, if filenames.txt is created on another OS it can potentially fail because of the various line endings that are used, either \r, \n or \r\n as explained in this answer. To ensure the solution is more robust across different OS/platforms then a check for which line ending is used will be necessary before split() is utilized to make an array.

The following shows how to achieve that (Note: The gist is intentionally verbose for explanation purposes, and should probably be refactored):

module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json')
    });

    grunt.registerTask('createProducts', 'Create product banners', function() {
        this.async();

        var fs = require('fs'),
            fileNameList = 'filenames.txt',
            str = fs.readFileSync(fileNameList, 'utf8'),
            arr = [];

        // Warn if filename.txt does not include any text.
        if (!str) {
            console.warn('Error: ' + fileNameList + ' is empty');
            process.exit(1);
        }

        // Check which line ending is used before splitting.
        if (str.indexOf('\r\n') !== -1) { // CR + LF - filenames.txt was created on Windows.
            arr = str.split('\r\n');
        } else if (str.indexOf('\n') !== -1) { // LF - filenames.txt was created on Unix/Mac OS X.
            arr = str.split('\n');
        } else if (str.indexOf('\r') !== -1) { // CR - filenames.txt was created on Mac OS before X.
            arr = str.split('\r');
        } else { // Only one line exists in filenames.txt
            arr.push(str);
        }

        for (var i in arr) {
            var newFilePath = arr[i].split('_')[1] + '/' + arr[i].split('_')[0] + '/' + arr[i];
            grunt.file.write(newFilePath, '<html></html>');
            console.log('Created file: ' + newFilePath);
        }
    });

};
Community
  • 1
  • 1
RobC
  • 22,977
  • 20
  • 73
  • 80