Skip to content
This repository has been archived by the owner on Jul 1, 2021. It is now read-only.

examples_javascript

Marcel Kloubert edited this page May 6, 2017 · 8 revisions

Home >> Examples >> JavaScript

JavaScript

UglifyJS

Uglify files BEFORE deploy

The following example shows how to use a data transformer script to uglify JavaScript files with build-in UglifyJS BEFORE they are uploaded to a SFTP server.

The example is also compatible with all other targets that support transformer setting.

First setup your settings.json file inside your .vscode folder with a package definition:

{
    "deploy": {
        // ...

        "packages": [
            {
                "name": "My JavaScript files to uglify",
                "files": [
                    "/**/*.js"
                ]
            }
        ],

        // ...
    }
}

Then define the target to deploy to:

{
    "deploy": {
        // ...

        "targets": [
            {
                "name": "My SFTP server",
                "type": "sftp",

                "host": "mysite.example.com",
                "user": "mkloubert", "password": "P@assword123!",

                "transformer": "./.deploy/uglifyjs.js",
                "transformerOptions": {
                    "exclude": [
                        "/**/*.min.js",
                        "/**/*.pack.js"
                    ]
                }
            }
        ],

        // ...
    }
}

As you can see, the transformer property defines a script, called uglifyjs.js, in the .deploy sub folder of your workspace:

var FS = require('fs');  // https://nodejs.org/api/fs.html
var Path = require('path');  // https://nodejs.org/api/path.html
var vscode = require('vscode');  // https://code.visualstudio.com/docs/extensionAPI/vscode-api

/**
 * Is invoked BEFORE file is going to
 * be send to remote target.
 * 
 * @return {Promise} The promise with the (uglyfied) data.
 */
exports.transformData = function(ctx) {
    return new Promise(function(resolve, reject) {
        var compilers = ctx.require('./compilers');  // https://mkloubert.github.io/vs-deploy/modules/_compilers_.html
        var glob = ctx.require('glob');  // https://www.npmjs.com/package/glob
        var helpers = ctx.require('./helpers');  // https://mkloubert.github.io/vs-deploy/modules/_helpers_.html
        var tmp = ctx.require('tmp');  // https://www.npmjs.com/package/tmp
        var workflows = ctx.require('node-workflows');  // https://www.npmjs.com/package/node-workflows

        // from "transformerOptions" property
        // of the underlying target
        var options = ctx.options || {};

        var completed = function(err, resultData) {
            if (err) {
                reject(err);
            }
            else {
                resolve(resultData);
            }
        };

        var session = {};
        var destroySession = function(err, resultData) {
            var destroyWF = workflows.create();

            // remove temp files
            [ session.sourceFile, session.outputFile ].filter(function(tempFile) {
                return !helpers.isEmptyString(tempFile);
            }).forEach(function(tempFile) {
                // first check if 'tempFile' exists
                destroyWF.next(function(wfCtx) {
                    return new Promise(function(res, rej) {
                        FS.exists(tempFile, function(exists) {
                            res( exists );  // s. wfCtx.previousValue
                                            // below
                        });
                    });
                });

                // now remove 'tempFile'
                // if exists
                destroyWF.next(function(wfCtx) {
                    var exists = wfCtx.previousValue;
                    
                    return new Promise(function(res, rej) {
                        if (exists) {
                            FS.unlink(tempFile, function(err) {
                                if (err) {
                                    //TODO: log
                                }

                                res();
                            });
                        }
                        else {
                            res();  // does not exist (anymore)
                        }
                    });
                });
            });

            destroyWF.start().then(function() {
                completed(err, resultData);
            }, function(e) {
                //TODO: log

                completed(err, resultData);
            });
        };

        // define the tasks todo
        var wf = workflows.create();

        // [1] initialize workflow result with
        //     input data
        wf.next(function(wfCtx) {
            wfCtx.result = ctx.data;  // initialize with input data
        });

        // [2] check if file is excluded
        wf.next(function(wfCtx) {
            return new Promise(function(res, rej) {
                if (options) {
                    // collect glob patterns and files
                    var excludePatterns = helpers.asArray(options.exclude)
                                                 .map(function(p) { 
                                                         return helpers.toStringSafe(p);
                                                      })
                                                 .filter(function(p) {
                                                            return '' !== p.trim();
                                                         });
                    excludePatterns = helpers.distinctArray(excludePatterns);

                    var excludedWF = workflows.create();

                    excludedWF.next(function(wfCtx2) {
                        wfCtx2.result = false;  // isExcluded
                    });

                    // create a glob check
                    // for each pattern
                    excludePatterns.forEach(function(pattern) {
                        excludedWF.next(function(wfCtx2) {
                            return new Promise(function(res2, rej2) {
                                glob(pattern, {
                                    absolute: true,
                                    cwd: vscode.workspace.rootPath,
                                    dot: true,
                                    nodir: true,
                                    root: vscode.workspace.rootPath,
                                }, function(err, filesToExclude) {
                                    if (err) {
                                        rej2(err);
                                    }
                                    else {
                                        // normalize paths of
                                        // found files
                                        filesToExclude = filesToExclude.map(function(f) {
                                            return normalizePath(ctx, f); 
                                        });

                                        if (filesToExclude.indexOf( normalizePath(ctx, ctx.context.file) ) > -1) {
                                            // marked as "excluded"
                                            // and stop next patterns

                                            wfCtx2.result = true;
                                            wfCtx2.finish();
                                        }

                                        res2();
                                    }
                                });
                            });
                        });
                    });

                    excludedWF.start().then(function(isExcluded) {
                        if (isExcluded) {
                            wfCtx.finish();  // do not uglify this
                        }

                        res();
                    }, function(err) {
                        rej(err);  // check failed
                    });
                }
                else {
                    res();  // nothing defined
                            // for exclusion
                }
            });
        });

        // [3] define temp files for the compiler
        wf.next(function(wfCtx) {
            return new Promise(function(res, rej) {
                tmp.tmpName({
                    keep: true,
                    prefix: 'vsd-uglifyjs-',
                    postfix: '.js',
                }, function(err, tempFile) {
                    if (err) {
                        rej(err);  // could not define temp
                                   // file for source data
                    }
                    else {
                        session.sourceFile = tempFile;

                        var tempFileDir = Path.dirname(tempFile);
                        var tempFileExtension = Path.extname(tempFile);
                        var tempFileBaseName = Path.basename(tempFile, tempFileExtension);

                        // the generated output file
                        // will have '.min.js' as extension
                        session.outputFile = Path.join( tempFileDir,
                                                        tempFileBaseName + '.min' + tempFileExtension );

                        res();
                    }
                });
            });
        });

        // [4] fill source file with unhandled
        //     data from ctx.data
        wf.next(function(wfCtx) {
            return new Promise(function(res, rej) {
                FS.writeFile(session.sourceFile, ctx.data, function(err) {
                    if (err) {
                        rej(err);  // could not write to source file
                    }
                    else {
                        res();
                    }
                });
            });
        });

        // [5] run UglifyJS for
        //     session.sourceFile
        wf.next(function(wfCtx) {
            return new Promise(function(res, rej) {
                compilers.compileUglifyJS({
                    files: [ session.sourceFile ],
                    deleteSources: true,
                }).then(function() {
                    res();
                }, function(err) {
                    rej(err);  // compiler error
                });
            });
        });

        // [6] read generated, ugly JavaScript
        wf.next(function(wfCtx) {
            return new Promise(function(res, rej) {
                FS.readFile(session.outputFile, function(err, resultData) {
                    if (err) {
                        rej(err);  // could not read generated
                                   // data by UglifyJS compiler
                    }
                    else {
                        wfCtx.result = resultData;  // update with
                                                    // uglified data

                        res();
                    }
                });
            });
        });

        // start "uglify" workflow
        wf.start().then(function(resultData) {
            destroySession(null, resultData);
        }, function(err) {
            //TODO: log

            destroySession(err);  // something went wrong
        });
    });
};

/**
 * Is invoked after file
 * has been downloaded from remote.
 * 
 * @return {Buffer} Currently: The source data from remote.
 */
exports.restoreData = function(ctx) {
    // currently there is no chance
    // to beautify the data back
    return ctx.data;
};


function normalizePath(ctx, p) {
    var helpers = ctx.require('./helpers');  // https://mkloubert.github.io/vs-deploy/modules/_helpers_.html
    var vscode = require('vscode');  // https://code.visualstudio.com/docs/extensionAPI/vscode-api

    if (helpers.isNullOrUndefined(p)) {
        return p;
    }

    p = helpers.toStringSafe(p);

    if (!Path.isAbsolute(p)) {
        p = Path.join(vscode.workspace.rootPath, p);
    }

    p = Path.resolve(p);
    p = helpers.replaceAllStrings(p, Path.sep, '/');

    return p;
}

That's all.

Now all of your .js files will be uglified, except the ones defined by the glob patterns in the transformerOptions.exclude property of the upper target.

It also works for single files.

Clone this wiki locally