Пользовательский плагин Gulp.js: TypeError "Путь должен быть строкой. Получено не определено"

Я нашел несколько тем на Stackru о TypeError: Path must be a string, хотя я не смог применить предложенные решения к моему делу.

Я пытаюсь создать плагин Gulp, который подключается к (платному) сервису javascriptobfuscator.com и использую их POST API, чтобы запутать мой JS.

Задание Gulp выглядит так:

var gulp = require('gulp'),
    jso = require('./jsobfuscator');

gulp.task('jso', function() {
    // .jsx contain extendscript language, which as far as 
    // the service is concerned, could be treated as .js
    return gulp.src('.src/app/jsx/photoshop.jsx')
               .pipe(jso())
               .pipe(gulp.dest('./dist'))
});

jsobfuscator.js Файл содержит следующий код:

var through = require('through2'),
    http = require('http'),
    gutil = require('gulp-util'),
    Readable = require('stream').Readable

module.exports = function() {
  /**
   * @this {Transform}
   */

    var transform = function(file, encoding, callback) {

        var that = this;

        var proj = {};
        proj.APIKey = "/* MyAPIKey*/";
        proj.APIPwd = "/* MyAPIPwd*/";
        proj.Name = "DoubleUSM";
        proj.ReorderCode = true;
        proj.ReplaceNames = true;
        proj.EncodeStrings = true;
        proj.MoveStrings = true;
        proj.MoveMembers = true;
        proj.DeepObfuscate = true;
        proj.SelfCompression = true;
        // Unsure about these two...
        proj.CompressionRatio = "Auto";
        proj.OptimizationMode = "Auto";

        var appJS = new Object();
        appJS.FileName = "app.js";
        appJS.FileCode = file.contents.toString('utf8');
        // Will try to implement multiple files later on
        proj.Items = [appJS];

        var postData = JSON.stringify(proj);

        // Length is OK
        gutil.log ("Length: " + Buffer.byteLength(postData, 'utf-8'))

        var options = {
            host: 'service.javascriptobfuscator.com',
            path: '/HttpApi.ashx',
            method: 'POST',
            headers: { 
                'Content-Type': 'text/json',
                'Content-Length': Buffer.byteLength(postData, 'utf-8')
            }
        };

        callback = function(response) {
            response.setEncoding('utf8');

            var str = '';
            response.on('data', function(chunk) {
                str += chunk;
            });

            response.on('end', function() {
                // I get the correct response here!
                gutil.log(JSON.stringify(JSON.parse(str), null, '  '));

                var resObj = JSON.parse(str);
                // Converting the received string into a Buffer
                var fileStream = new Readable();
                // resObj.Items[0].FileCode is where the obfuscated code belongs
                fileStream.push(resObj.Items[0].FileCode);
                fileStream.push(null);
                that.push(fileStream);
                callback();
            });
        }

        var req = http.request(options, callback);
        req.write(postData);
        req.end();
    };

    return through.obj(transform);
};

Когда я запускаю задачу gulp, я, очевидно, получаю правильный ответ из кода javascriptobfuscator (как вы можете видеть ниже), но что-то не так в той части, где я передаю файл к месту назначения, потому что я получаю:

(master)*  gulp jso
[15:13:30] Using gulpfile ~/Dropbox/Developer/PROJECTS/DOUBLE USM/gulpfile.js
[15:13:30] Starting 'jso'...
[15:13:31] Lenght: 21897
[15:13:32] {
  "Type": "Succeed",
  "Items": [
    {
      "FileName": "app.js",
      "FileCode": /* ... Long, horrible, properly obfuscated blob here... */
    }
  ],
  "ErrorCode": null,
  "Message": null,
  "FileName": null,
  "LineNumber": null,
  "ExceptionToString": null
}
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:1146:7)
    at DestroyableTransform.saveFile [as _transform] (/Users/davidebarranca/Dropbox/Developer/PROJECTS/DOUBLE USM/node_modules/vinyl-fs/lib/dest/index.js:36:26)
    at DestroyableTransform.Transform._read (/Users/davidebarranca/Dropbox/Developer/PROJECTS/DOUBLE USM/node_modules/vinyl-fs/node_modules/readable-stream/lib/_stream_transform.js:184:10)
    at DestroyableTransform.Transform._write (/Users/davidebarranca/Dropbox/Developer/PROJECTS/DOUBLE USM/node_modules/vinyl-fs/node_modules/readable-stream/lib/_stream_transform.js:172:12)
    at doWrite (/Users/davidebarranca/Dropbox/Developer/PROJECTS/DOUBLE USM/node_modules/vinyl-fs/node_modules/readable-stream/lib/_stream_writable.js:237:10)
    at writeOrBuffer (/Users/davidebarranca/Dropbox/Developer/PROJECTS/DOUBLE USM/node_modules/vinyl-fs/node_modules/readable-stream/lib/_stream_writable.js:227:5)
    at DestroyableTransform.Writable.write (/Users/davidebarranca/Dropbox/Developer/PROJECTS/DOUBLE USM/node_modules/vinyl-fs/node_modules/readable-stream/lib/_stream_writable.js:194:11)
    at DestroyableTransform.ondata (/Users/davidebarranca/Dropbox/Developer/PROJECTS/DOUBLE USM/node_modules/through2/node_modules/readable-stream/lib/_stream_readable.js:546:20)
    at emitOne (events.js:96:13)

По общему признанию я не эксперт плагинов Gulp - я пытаюсь создать функциональность для моего личного использования. Похоже, я довольно близко, но я застрял в этом тупике на некоторое время, и мне нужна помощь более опытных.

заранее спасибо


Наполовину рабочее обновление

Модификация response.on('end') Обратный вызов следующим образом, я смог наконец написать файл:

response.on('end', function() {

    var resObj = JSON.parse(str);

    var fileStream = new Readable();
    fileStream.push(resObj.Items[0].FileCode);
    fileStream.push(null);

    // Manually creating a new Vinyl file
    var jsFile = new Vinyl({
        cwd: file.cwd,
        base: file.base,
        path: file.path,
        contents: fileStream
    });

    return that.push(jsFile);
    // callback();
});

Я должен был закомментировать финал callback()Тем не менее, в противном случае я бы столкнулся с ошибкой, указывающей на response.setEncoding('utf8'); линия, будучи response не определено.

Проблема сейчас в том, что кажется, что задача, в некотором роде, никогда не заканчивается. Консоль говорит Starting 'jso'..., файл записан, консоль возвращается, но нет Finished 'jso' after nn msи, что хуже всего, я совершенно не в состоянии связать больше глоточных задач потом, так что если я:

gulp.task('jso1', function() {
    return gulp.src('.src/app/jsx/photoshop.jsx')
               .pipe(jso())
               .pipe(gulp.dest('./dist'))
});

gulp.task('jso2', ['jso1'], function() {
    return gulp.src('.src/app/app.js')
               .pipe(jso())
               .pipe(gulp.dest('./dist'))
});

// then in the Terminal:
$ gulp jso2

Единственное, что я получаю, это jso1 начало, очевидно работающее, но никогда не заканчивающееся; jso2 никогда не бежит.


1 ответ

Многочасовые царапины на голове заставили меня прийти к ответу. Вкратце, в моем плагине Gulp:

  1. Я должен был вернуть новый виниловый файл в response.on('end') обратный вызов: файл contents свойство должно быть потоком от ответа сервера.
  2. Основной недостаток моего полу-рабочего решения в том, что я дал одно и то же имя ( callback ) две очень разные функции. Одним из них является callback от начального transform функция; другой является http.request Перезвоните. Закомментируем последнее callback() позвони, после return that.push(jsFile); помешал завершению всей задачи Gulp.

Полный рабочий код выглядит следующим образом:

var through  = require('through2'),
    http     = require('http'),
    gutil    = require('gulp-util'),
    Readable = require('stream').Readable,
    Vinyl    = require('vinyl');

module.exports = function() {

    var transform = function(file, encoding, callback) {

        if (file.isNull()) { return callback(null, file) }

        var that = this;

        var proj = {};
        proj.APIKey = "/* MyAPIKey */";
        proj.APIPwd = "/* APIPwd */";
        proj.Name = "Project";
        proj.ReorderCode = true;
        proj.ReplaceNames = true;
        proj.EncodeStrings = true;
        proj.MoveStrings = true;
        proj.MoveMembers = true;
        proj.DeepObfuscate = true;
        proj.SelfCompression = true;
        proj.CompressionRatio = "Auto";
        proj.OptimizationMode = "Auto";

        var appJS = new Object();
        appJS.FileName = "app.js";
        appJS.FileCode = file.contents.toString('utf8');

        proj.Items = [appJS];

        var postData = JSON.stringify(proj);

        var options = {
            host: 'service.javascriptobfuscator.com',
            path: '/HttpApi.ashx',
            method: 'POST',
            headers: { 
                'Content-Type': 'text/json',
                'Content-Length': Buffer.byteLength(postData, 'utf-8')
            }
        };

        /* Renamed to avoid confusion with transform's callback */
        postCallback = function(response) {

            response.setEncoding('utf8');
            var str = '';
            response.on('data', function(chunk) {
                str += chunk;
            });

            response.on('end', function() {

                var resObj = JSON.parse(str);
                var fileStream = new Readable();
                fileStream.push(resObj.Items[0].FileCode);
                fileStream.push(null);

                /* Return a new Vinyl File */
                var jsFile = new Vinyl({
                    cwd: file.cwd,
                    base: file.base,
                    path: file.path,
                    contents: fileStream
                });

                that.push(jsFile);
                callback();
            });
        }

        var req = http.request(options, postCallback);
        req.write(postData);
        req.end();

    };

    return through.obj(transform);
};

Возможно, есть лучшие способы вернуть файл Vinyl без создания нового.

Другим улучшением было бы сгруппировать все исходные файлы и сделать один http-вызов API javascriptobfuscator (фактически можно добавить несколько файлов в proj.Items массив), но я понятия не имею, как сгруппировать файлы в исходном коде, передать их все сразу плагину Gulp, а затем разделить их позже.

Другие вопросы по тегам