Search code examples
javascriptnode.jsgulpgulp-uglify

Uglify several .js to differents folders (but with same scope)


Firstly, sorry my english.

I'm developing the frontend of a system where I've some js files that will be concatened and uglified to app.js. That's ok. But, some js files will be load by demand, that is, the files will be at differents folders, loaded by request, and consume the uglified app.js . My problem is that I need uglify global variables and name functions, and when I will uglify my scripts, the files not contain the "context" equal.

My struct:

|project
  |scripts
    |core
      *.js
    |utils
      *.js
    |pages
      *.js
    app.js
    vendor.js
  |templates
    *.hbs // this files will be "compiled" to js with gulp-handlebars

My build result:

|public
  |assets
    |js
      |pages // separated files, uglified with app.js scope
        *.js
      |templates // separated files, uglified with app.js scope
        *.js
      app.js // concat and uglify app.js, core and utils directories
      vendor.js // uglify separed keeping global variables (bower)

For ease of explanation, consider that public/assets/js/app.js have a function translate(a). This just receive variable 'a' and return the value. Ex:

function translate(a) {
  return a;
}

Firstly, the file app.js is the only loaded in my html. Depending that locale of site, files of pages and templates directories will be loaded dynamically to the html, consuming app.js functions.

Ex: public/assets/js/pages/*.js or public/assets/js/templates/*.js:

function consumeApp(a) {
  return translate(a);
}

My problem is that the function "translate" is not defined for js files in pages and template directories, because the uglify not is working for different files that need the same context/scope.

My dev dependences (package.json):

"devDependencies": {
    "babel-core": "^6.4.0",
    "babel-preset-es2015": "^6.3.13",
    "babel-register": "^6.5.2",
    "bower": "^1.8.0",
    "del": "^1.1.1",
    "gulp": "^3.9.1",
    "gulp-autoprefixer": "^3.1.1",
    "gulp-babel": "^6.1.1",
    "gulp-bower": "0.0.13",
    "gulp-browserify": "^0.5.1",
    "gulp-cache": "^0.4.6",
    "gulp-concat": "^2.6.1",
    "gulp-cssnano": "^2.1.2",
    "gulp-declare": "^0.3.0",
    "gulp-eslint": "^3.0.1",
    "gulp-handlebars": "^4.0.0",
    "gulp-htmlmin": "^3.0.0",
    "gulp-imagemin": "^3.2.0",
    "gulp-include": "^2.3.1",
    "gulp-load-plugins": "^1.5.0",
    "gulp-plumber": "^1.1.0",
    "gulp-precompile-handlebars": "^2.0.5",
    "gulp-sass": "^3.1.0",
    "gulp-size": "^2.1.0",
    "gulp-sourcemaps": "^2.6.0",
    "gulp-sync": "^0.1.4",
    "gulp-uglify": "^2.1.2",
    "gulp-wrap": "^0.13.0",
    "handlebars": "^4.0.10",
    "jscs": "^3.0.7",
    "uniq": "^1.0.1",
    "webpack": "^2.6.1",
    "webpack-stream": "^3.2.0"
  }

My gulpfile (only uglify section):

const gulp = require('gulp');
const gulpsync = require('gulp-sync')(gulp);
const gulpLoadPlugins = require('gulp-load-plugins');
const del = require('del');
const concat = require('gulp-concat');
const webpack = require('webpack-stream');

const $ = gulpLoadPlugins();

// .... uglify tasks run after all be in public

gulp.task('scripts:uglify', gulpsync.sync([
    'scripts:uglify-vendor',
    'scripts:uglify-app'
]));

gulp.task('scripts:uglify-app', () => {
    return gulp.src([
        'public/assets/js/**/*js',
        '!public/assets/js/vendor.js',
    ]).pipe($.uglify({
        mangle: {
            toplevel: true
        }
    }))
        .pipe(gulp.dest('public/assets/js'));
});

gulp.task('scripts:uglify-vendor', () => {
    return gulp.src('public/assets/js/vendor.js')
        .pipe($.uglify({mangle: false}))
        .pipe(gulp.dest('public/assets/js'));
});

Somebody know why? Thanks.


Solution

  • I created a method in gulpfile, using node.js, that "randomizes" global variables and specific functions to resolve my problem. To do this, I separated all global variables in a unique file called ./scripts/core/_variables.js where each variable is declared line for line. I mapped all global variables dinamically, concating with functions name and I replace in the files allready builded in ./public/assets/js/**/*.js. The algorithm bellow can be configured with functions name and directories that can be randomfy.

    const fs = require('fs'); // added
    const prettyjson = require('prettyjson'); // added
    
    const consoleLog = (msg) => {
        console.log(prettyjson.render(msg));
    };
    
    gulp.task('run-build', gulpsync.sync([
        'myTasks', // tasks to run build
    ]]), () => {
        return gulp.src('public/assets/**/*').pipe($.size({title: 'build', gzip: true}));
    });
    
    gulp.task('build', gulpsync.sync(['run-build', 'randomly']), () => {
        return true;
    });
    
    gulp.task('randomly', () => {
        return new Promise( function(resolve) {
    
            // set here the functions name to randomly
            function getFunctions() {
                consoleLog('get functions name');
                return [
                    'translate',
                    'myFirstFunction',
                    'mySecondFunction'
                ]
            }
    
            // get automatically variables in ./scripts/core/_variables.js
            function getVariables() {
                consoleLog('start global variables loader');
    
                var fileContent = fs.readFileSync('./scripts/core/_variables.js', 'utf8');
                var a = fileContent.split('=');
                var consts = [],
                    lets = [];
                for (var i = 0; i < a.length; i++) {
                    var c = a[i].split('const '),
                        l = a[i].split('let ');
                    if (c[1])
                        consts.push(c[1].toString().trim());
                    if (l[1])
                        lets.push(l[1].toString().trim());
                }
                consoleLog('finished global variables loader');
                return consts.concat(lets);
            }
    
            // set random unique names to variables and functions
            function setRandomNames(variables) {
                return new Promise( function(resolve) {
                    consoleLog('start fake names generator');
                    var fakeNames = [];
                    var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    
                    function existFakeName(name) {
                        for(var i in fakeNames)
                            if(fakeNames[i].fake === name)
                                return true;
                        return false;
                    }
    
                    function newNames() {
                        return new Promise( function(resolve) {
                            var name = '';
                            for (var i = 0; i < 10; i++)
                                name += possible.charAt(Math.floor(Math.random() * possible.length));
                            if (fakeNames.length <= variables.length && !existFakeName(name)) {
                                var item = fakeNames.length;
                                fakeNames.push({
                                    name: variables[item],
                                    fake: name
                                });
                                if (fakeNames.length === variables.length) {
                                    consoleLog(fakeNames);
                                    consoleLog('finished fake names generator');
                                    resolve();
                                }
                                else
                                    resolve(newNames());
                            }
                            else
                                resolve(newNames());
                        });
                    }
                    newNames().then(() => {
                        resolve(fakeNames);
                    })
                });
            }
    
            function listDirectory(dir) {
                return new Promise((resolve) => {
                    var listFiles = fs.readdirSync(dir);
                    var response = [];
                    for (var i in listFiles) {
                        response.push(dir + listFiles[i]);
                    }
                    resolve(response);
                });
            }
            function replaceFile(file, fakes) {
                return new Promise( function(resolve) {
                    // consoleLog('start replace file ' + file);
    
                    var fileContent = fs.readFileSync(file, 'utf8');
                    for (var i in fakes)
                        fileContent = fileContent.split(fakes[i].name).join(fakes[i].fake);
                    resolve(writeFile(file, fileContent));
                })
            }
            function writeFile(file, fileContent) {
                return new Promise( function(resolve) {
                    fs.writeFile(file, fileContent, function (err) {
                        if (err) {
                            consoleLog('error ' + file);
                            resolve();
                        }
    
                        // consoleLog('finished replace file and saved: ' + file);
                        resolve();
                    });
                });
            }
    
            consoleLog('start randomly');
            setRandomNames(getVariables().concat(getFunctions())).then( function(fakes) {
                consoleLog('start list directories');
                Promise.all([
                    listDirectory('./public/assets/js/pages/')
                ]).then( function(response) {
    
                    consoleLog('finished list directories');
    
                    var files = ['./public/assets/js/app.js']; // change app.js
                    for (var i in response)
                        files = files.concat(response[i]);
    
                    var promises = [];
                    for(var file in files)
                        promises.push(replaceFile(files[file], fakes));
    
                    Promise.all(promises).then( function() {
                        consoleLog('all files replaced and saved');
                        consoleLog('finished randomly');
                        resolve();
                        return true;
                    });
                });
            });
        });
    });