Search code examples
node.jsreactjsgulpecmascript-6browserify

How to create a new directory with Browserify if it does not exist?


I tried to save a compiled js bundle file to a production directory with Browserify and Gulp.
But it seems that Browserify can't find the valid path to the output directory.
Below is the code snippet:

  browserify({ debug: true })
    .transform(babelify.configure({
      presets: ['es2015', 'react']
    }))
    .require('./client/client.js', { entry: true })
    .bundle()
    .on('error', (err) => {
      console.log(err);
    })
    .on('end', () => {
      console.timeEnd();
    })
    .pipe(gulp.dest('dist/public/bundle.js'));

And I am getting the following error:

[19:37:57] Finished 'lint' after 14 s
path.js:7
    throw new TypeError('Path must be a string. Received ' + inspect(path));
    ^

TypeError: Path must be a string. Received undefined
    at assertPath (path.js:7:11)
    at Object.resolve (path.js:1148:7)
    at DestroyableTransform.saveFile [as _transform] 

I tried to use the manual way by:

.pipe(fs.createWriteStream(`${__dirname}/dist/public/bundle.js`));

And it doesn't work. But below works:

.pipe(fs.createWriteStream(`${__dirname}/dist/bundle.js`));

Here folder 'dist' exists in the directory but not 'public' in the 'dist'.
I think this is the reason, but how can I resolve it to save the bundle file to 'dist/public'?


Solution

  • Your two attempts fail for different reasons.

    Your first attempt using gulp.dest() fails because browserify() returns a simple character stream, whereas gulp.dest() can only work on streams of vinyl objects. You need to use the vinyl-source-stream module to convert the char stream to a vinyl stream. After that gulp.dest() will work and will create any folders that don't exist yet:

    var browserify = require('browserify');
    var source = require('vinyl-source-stream');
    
    browserify({ debug: true })
      .transform(babelify.configure({
        presets: ['es2015', 'react']
      }))
      .require('./client/client.js', { entry: true })
      .bundle()
      .on('error', (err) => {
        console.log(err);
      })
      .on('end', () => {
        console.timeEnd();
      })
      .pipe(source('bundle.js')) 
      .pipe(gulp.dest('dist/public/'));
    

    Your second attempt only uses char streams, but fails because fs.createWriteStream() can only create the bundle.js file itself, not any of the necessary parent directories. You'll have to do that manually beforehand. I suggest the mkdirp module which recursively creates directory structures of arbitrary depth if they don't exist:

    var browserify = require('browserify');
    var mkdirp = require('mkdirp');
    var fs = require('fs');
    
    mkdirp('dist/public/', function(err) {
      if (err) { /* handle error somehow */ }
    
      browserify({ debug: true })
        .transform(babelify.configure({
          presets: ['es2015', 'react']
        }))
        .require('./client/client.js', { entry: true })
        .bundle()
        .on('error', (err) => {
          console.log(err);
        })
        .on('end', () => {
          console.timeEnd();
        })
        .pipe(fs.createWriteStream('dist/public/bundle.js'));
    });
    

    Which one of these two solutions you choose is up to you, but if this is code that you're running from a gulpfile then I suggest you use the first one.