My totally non-hacky ways to control the order in which JavaScript files are concatenated

Last modified March 14, 2019
.* :☆゚

If you’re developing websites, you likely want to minify and concatenate your files for production. The process of minifying/concatenating is often used before putting a site live to save extra kB when loading a page. Processing CSS is a little easier with Sass - the order can easily be controlled through a single stylesheet.

Processing javascript is a little more annoying however in that it is a little harder to control the order they are processed in. If you’re using something like Gulp, Grunt, or even Codekit, you can sometimes run into the problem of dependencies being concatenated before your main script file.

These are the most efficient ways I’ve used to overcome this problem.


Rename your script file

Developers often have scripts in a /scripts folder in their project, with one main script file for the logic, and the other files as dependencies or various other bits of code that are being imported or relied on in the main file.

For example, here’s the src folder of this entire website(not including the html/markdown):

src
├── fonts
│   ├── overpass-extrabold-webfont.woff
│   ├── overpass-extrabold-webfont.woff2
│   ├── overpass-regular-webfont.woff
│   ├── overpass-regular-webfont.woff2
│   ├── overpassmono-regular-webfont.woff
│   ├── overpassmono-regular-webfont.woff2
│   ├── spacemono-regular-webfont.woff
│   └── spacemono-regular-webfont.woff2
├── images
│   ├── moon.png
│   └── sun.png
├── js
│   ├── isotope.js
│   ├── nav.js
│   └── zcript.js
├── lambda
└── sass
    ├── _elements.scss
    ├── _forms.scss
    ├── _includemedia.scss
    ├── _layout.scss
    ├── _media.scss
    ├── _mixins.scss
    ├── _modularscale.scss
    ├── _navigation.scss
    ├── _normalize.scss
    ├── _syntax.scss
    ├── _typography.scss
    ├── _variables.scss
    ├── modularscale
    │   ├── _function.scss
    │   ├── _pow.scss
    │   ├── _respond.scss
    │   ├── _round-px.scss
    │   ├── _settings.scss
    │   ├── _sort.scss
    │   ├── _strip-units.scss
    │   ├── _sugar.scss
    │   ├── _target.scss
    │   └── _vars.scss
    └── style.scss

You might’ve noticed I named my main file zcript.js. This is not a mistake. 😆

The thing is, what you call your main script file doesn’t actually matter. The way concatenation usually works is it will process files alphabetically, and numerically starting at 0. This is why renaming script.js to zcript.js works so well for me. What JS library am I using that starts with a z? (that exist yet…😂)

For personal sites and projects where it’s just you working on it all, I don’t really see the problem in renaming script to something as obvious as this.

Best part of this method means there is zero other config to implement, meaning you are free to work on your website instead of yet another build process!

Use globs

WHen you work with other people, maybe the name of your main file actually does matter. You want it to be accessible and meaningful, and obvious that it is the main entry point for all the javascript code.

At work, I just call my main file base.js, I’ve seen other devs call it functions.js, scripts.js, or app.js. Doesn’t really matter, as long as your setup can process and understand it.

I use globs in my tasks to ensure my main entry point is processed last.

According to gulp,

A glob is a string of literal and/or wildcard characters used to match filepaths. Globbing is the act of locating files on a filesystem using one or more globs.

As an example, here is the scripts task I use at work:

function scripts() {
    return gulp.src([
        //process base.js last, ignore files prefixed with a _
        projectcwd + config.jsSRC + '/**/!(base)*.js',
        projectcwd + config.jsSRC + '/base.js',

        '!' +  projectcwd + config.jsSRC + '/**/_*/**',
        '!' +  projectcwd + config.jsSRC + '/**/_*',
        '!' +  projectcwd + config.jsSRC + '/**/imports/**'
    ], { allowEmpty: true })
        .pipe(plumber({
            errorHandler: handleError
        }))
        .pipe(babel({presets: [require.resolve('@babel/preset-env')]}))
        .pipe(concat('script.js'))
        .pipe(gulp.dest(projectcwd+config.jsDST))
        .pipe(rename({
            suffix: '.min'
        }))
        .pipe(uglify())
        .pipe(assetManifest({
            manifestFile: projectcwd + config.manifestPath,
            bundleName: 'js',
            includeRelativePath: true
        }))
        .pipe(gulp.dest(projectcwd+config.jsDST))
        .pipe(browsersync.stream())
        .on('end', function () {
            gutil.log(
                gradient.fruit("\n"),
                gradient.fruit("    .^====^.   \n"),
                gradient.fruit("   =( ^--^ )=  ♥ script.js & script.min.js minified to " + projectcwd + config.jsDST + "\n"),
                gradient.fruit("    /      \\   /~\n"),
                gradient.fruit("  +( |    | )//\n" )
            );
        });
}

The globs I’m using tells gulp to ignore all files and folders prefixed with an underscore as well as the imports folder.

The order I wrote the globs in matter too - I asked it to process all files except base.js, then process base.js last.

I admit it is a bit complicated to get your head around but once you understand how you can traverse up/down a file tree it gets easier to know how to write useful globs.

There are a ton of examples out there that use glob techniques and I’m sure it would be easy enough to find a gulpfile that makes use of globs in some way. I highly recommend reading up the documentation on gulp too.

Use numbers

If the above is too much config for you, you can prefix all your js files with numbers in the order your want them processed. I know, many people will turn their nose up at this concept because it seems like a recipe for disaster(I mean, it could be, but only if you let it. You could just prefix all your dependencies with a 2, and your main file 1 to make it simpler). Honestly though, it actually does make some sense, and if I inherited a project with this concept, seeing numbered js files would sort of be understandable to me….however I would probably also question why the dev didn’t figure out a better method as well if it is a larger project.

Use a config file

You can use a custom config file to order your files. My colleague used to do this before webpack was a thing, and it did the job! Although personally I still think globbing is more time efficient and reliable (albeit a bit more time consuming to set up). Config files can be in JSON or JS, as long as it’s something that can be read by whatever you are using to process your files.

This is something I would be doing if I wanted to use a config file.