40

I want to create a compiled JavaScript file for my website. For development I would prefer to keep the JavaScript in separate files and just as part of my automated scripts concatenate the files into one and run the compressor over it.

My problem is that if I use the old DOS copy command it also puts in the EOF markers which the compressor complains about:

copy /A *.js compiled.js /Y

What are other people doing?

Ken
  • 77,016
  • 30
  • 84
  • 101
Phil Hannent
  • 12,047
  • 17
  • 71
  • 118
  • 1
    Phil, from your comment on my answer, I can see now that you don't get EOF markers (well, I got one with copy, at the end of the resulting file) but indeed the BOMs, because somehow you have written Unicode files. Flatten them to Ascii, you should get rid of them. – PhiLho Nov 19 '08 at 21:18
  • Indeed, throughout my day I work across Windows, Linux and Mac with many different languages. Unicode is the norm for me. – Phil Hannent Nov 26 '10 at 10:42

11 Answers11

35

I recommend using Apache Ant and YUI Compressor.

http://ant.apache.org/

http://yui.github.com/yuicompressor/

Put something like this in the Ant build xml. It will create two files, application.js and application-min.js.

<target name="concatenate" description="Concatenate all js files">
    <concat destfile="build/application.js">
        <fileset dir="src/js" includes="*.js" />
    </concat>
</target>

<target name="compress" depends="concatenate" description="Compress application.js to application-min.js">
    <apply executable="java" parallel="false">
        <filelist dir="build" files="application.js" />
        <arg line="-jar" />
        <arg path="path/to/yuicompressor-2.4.2.jar" />
        <srcfile />
        <arg line="-o" />
        <mapper type="glob" from="*.js" to="build/*-min.js" />
        <targetfile />
    </apply>
</target>
Ryan Stein
  • 7,930
  • 3
  • 24
  • 38
David Brockman
  • 567
  • 4
  • 6
  • 2
    This comes up with an `Unexpected element "{}target" {antlib:org.apache.tools.ant}target` error for me. Any ideas? – Jasdeep Khalsa Jan 09 '13 at 23:34
  • 4
    I stopped using Ant a few years ago. Now I use Node for these things. Take a look at this: http://mechanics.flite.com/blog/2012/06/19/why-we-use-node-dot-js-and-grunt-to-build-javascript/ – David Brockman Jan 11 '13 at 17:55
11

To copy without EOF use binary mode:

copy /B *.js compiled.js /Y

If the resulting file still has EOFs, that might have come from one of original files, it can be fixed by this variant:

copy /A *.js compiled.js /B /Y

/A removes trailing EOFs from original files if any and /B prevents appending EOF to the resulting file. If an EOF is not at the end, the source file will be truncated at it. The order of switches is important. If you write

copy /A *.js /B compiled.js /Y  

- EOFs in source files won't be removed but still resulting EOF won't be appended.

Try it yourself, thats where I get it. DOS commands are weird.

eugensk
  • 1,882
  • 1
  • 14
  • 20
  • Oddly the /B option made no difference to my files – Phil Hannent Nov 19 '08 at 11:25
  • I tried your suggestion, however I still get:  As a marker between where the files join... thanks for looking at this. – Phil Hannent Nov 19 '08 at 11:53
  • 2
    It seems I got it,  is a UTF8 prefix bytes. It should be at the very beginning of file, othervise it is treated as data and your compiler complaints on it. It is not EOF problem. And you can not use copy. – eugensk Nov 19 '08 at 12:30
6

In asp.net AJAX, you can use the 'CompositeScript' tag. This will combile all your scripts into 1 big js file, saving bandwidth by reducing the number of http 304s and possibly http 401s.

Sample:

 <asp:ScriptManager ID="ScriptManager1" runat="server">
        <CompositeScript>
            <Scripts>
                <asp:ScriptReference Path="~/Scripts/Script1.js" />
                <asp:ScriptReference Path="~/Scripts/Script2.js" />
                <asp:ScriptReference Path="~/Scripts/Script3.js" />
            </Scripts>
        </CompositeScript>
    </asp:ScriptManager>

For more info, see here: http://msdn.microsoft.com/en-us/library/cc488552.aspx

Punit Vora
  • 5,052
  • 4
  • 35
  • 44
6

This is a very old question, but I want to mention that there are also ways to concatenate javascript using javascript! with nodejs obviously... For example there are tools published as npm modules like this and there are also grunt and gulp plugins too.

I also want to mention a very, VERY, interesting technique that is being used in huge projects like jQuery and Modernizr. Both of this projects are entirely developed with requirejs modules and then they use the requirejs optimizer as a very smart concatenator. The interesting thing is that, as you can see, neither jQuery nor Modernizr needs on requirejs to work, and this happen because they erase the requirejs syntatic ritual in order to get rid of requirejs in their code. So they end up with a standalone library that was developed with requirejs modules!. Thanks to this they are able to perform cutsom builds of their libraries, among other advantages. Here is a blog post that explains all this more in detail.

Jay Edwards
  • 950
  • 1
  • 12
  • 21
Augusto Altman Quaranta
  • 1,526
  • 1
  • 21
  • 32
5

Install the compressor uglifyjs on your machine:

sudo npm -g install uglify-js

Then the following command can be used to concatenate and compress all js files.

cat myAppDir/*.js | uglifyjs > build/application.js
Fergal
  • 5,213
  • 6
  • 35
  • 44
4

We have created a mechanism consisting of the following parts:

  • minfication (for js and css)
  • aggregation in packages
  • caching (http status 304 stuff)
  • sending out original files for in development mode

It may be too much for your needs, but as to answer your question what others do, here is how it works:

  1. A request comes in at, say, /css.aspx?package=core
  2. We do a lookup of the packagename in a xml configuration file (which for instance declares that the package "core" contains the files /js/mootools.js and /js/swfobject.js)
  3. We check if minification is enabled. For instance, in a development environment we don't want the minified js contents to be served out, but instead write the original files. For js this is done by document.writes of script includes, and for css we write import rules.
  4. If minification is required (in production env) we do a check on the if-modified-since header from the request. If this client already has the minified contents, we send http header 304. If the client does require the contents, we check if we have minified contents in cache and serve that. Otherwise, we minify and send the result.

All this is broken up in separate services. There is a cache service injected in the jsminificationwriter service. This makes use of the original minificationservice that solely takes care of the minification rules.

What's nice in this approach is:

  • It forces our development teams to think in js/css "packages" and therefore properly split up functionality and distribute them over the pages that require them.
  • During development you are perfectly able to debug, getting proper files and line numbers.
  • You can hook up any other minification service implementation such as YUI and so forth. JsMin was only our first take.
  • It's a general approach that works for different content types.

Hope this helps. I can post some code fragments to illustrate it more if you like.

Martin Kool
  • 4,188
  • 4
  • 25
  • 36
3

You can also do:

type *.js > compiled.js
PhiLho
  • 40,535
  • 6
  • 96
  • 134
3

I'll second yuicompressor, but I use /packer/

http://johannburkard.de/blog/programming/javascript/automate-javascript-compression-with-yui-compressor-and-packer.html

It's been really excellent for me.

annakata
  • 74,572
  • 17
  • 113
  • 180
  • 1
    Packer uses client side compression, which can actually slow down the user, so it is not recommended – timmow May 18 '11 at 17:10
  • Or get Shrinker, it's the awesome packer compression utility for mac: http://itunes.apple.com/us/app/shrinker/id439567298?mt=12&ls=1# – JqueryToAddNumbers Jul 17 '11 at 06:30
1

I know this is a very old question, but for the sake of completeness I'll mention the option of going with Browserify. It allows you to build your project as different modules using NPM's require function to resolve dependencies, and then it resolves those dependencies and concatenates your entire project into an single file.

For example, say your project is called FooBar and you want to output a file called foobar.js. You'd create a main.js file as the entry point for the project, requiring all the modules that should be included.

main.js

require("./doFoo");
require("./doBar");

Then run:

browserify main.js -o foobar.js

Or, to do it automatically every time a source file changes you could also use Watchify.

watchify main.js -o foobar.js

Browserify will also resolve dependencies between modules. So for example, if doBar.js depends on doQux.js...

doBar.js

require("./doQux");
const doBar = ()=>{
    //Do some bar stuff.
}
exports.doBar = doBar;

Then Browserify will make sure to include doQux.js into foobar.js so that you won't have any broken dependencies.

Mig82
  • 4,856
  • 4
  • 40
  • 63
0

You can also try wro4j (web resource optimizer for java), which can be used as a build tool (maven plugin), runtime solution (using filter) or command line tool. It allows you to easily keep resources organized and handle the merging for you using a dozen of compressors for resources of bot types: js and css.

Defining resources to merge is easy as:

<groups xmlns="http://www.isdc.ro/wro">
  <group name="all">
    <css>/asset/*.css</css>
    <js>/asset/*.js</js>
  </group>
</groups>  

Disclaimer: I'm a commiter of this project.

Alex Objelean
  • 3,893
  • 2
  • 27
  • 36
0

This is how I resolved the very same task using glob:

const fs = require('fs');
const glob = require('glob');

const outputFile = './output/output.js';
const filesToConcat = './path/*.js';

// delete output file if exists (otherwise it will continue appending)
fs.unlink(outputFile, function(err) {
  console.log('output.js is removed');
});

// concat all js files into cam-benefits-ui.js
glob(filesToConcat, function(err, files) {
  files.forEach(file => {
    fs.readFile(file, 'utf8', function(err, data){
      fs.appendFileSync(outputFile, data);
    });
  });
});
Gokce Akcan
  • 61
  • 1
  • 3