Note: PackageJS is now deprecated in favor of my Node.js module PackagingJS at packagingjs and its gulp shell gulp-packagingjs. These modules have simpler rules for class definitions, are even more versatile in how you can write code, and run with node/gulp. They also are capable of producing source maps, automatic building (with gulp) and can work on all systems. Basically they're superior in every way.

What is PackageJS?

PackageJS is my own personal solution to a problem. That problem is that code organization in JS is very difficult compared to many languages. Anyone that has worked with languages with package and import concepts can attest that it makes organization so much easier. In fact, the dramatic shift that was going to be ECMAScript 4 used them extremely well before that version of the spec was abandoned.

There are many solutions to this, including languages like Haxe which can compile to JS. However, I wanted to use real normal JS...the only real change I wanted was the ability to wrap code into package blocks and isolate them to their own files, and use import statements to bring them (and any of their dependencies) in easily.

So that’s what PackageJS does. It allows you to separate out what you consider to be a “class” of code (we’ll just refer to them as classes even though it can be an object, function, ES6 class, whatever), namespace it with a package, and import it into any project via simple import statements. It basically then compiles all your code for a given project into one file of normal JS easily. It doesn’t modify your JavaScript: you still write normal JS code aside from using packaging, importing, and separate files per “class” for organizational purposes.

I’ve used PackageJS to help organize the creation of many large JS projects, like this save the date game for my wedding.

On top of that core concept of organizing JS code into individual files it also has many side functionalities that turn it into a very useful tool. For example even if a bunch of different classes all import one other class, it only gets compiled into the end file once. So it smartly makes sure that only the minimum amount of code is included with no effort on your part, meaning every class with any dependency can import/include as it sees fit without any worry of compiling in files more than once.

It also encapsulates the code of each class (as contained by its package block) in an IIFE in order to protect the global scope, but at the same time makes local vars representing every imported namespaced class just for that class so that it can utilize them without the namespaces, allowing complex namespacing without the negative effect of verbose code (i.e. new my.name.spaced.ClassName() can be called with new ClassName() to make it easy to deal with namespaces, but that’s ONLY true inside the packaged code that imported it, so it doesn’t pollute the global scope).

It also has include and using statements for bringing in and using normal non-PackageJS JavaScript files and making any namespaces in them just as easy to use.

It also can search for imported files in multiple locations, allowing you to draw some files from a repository of your reusable code and others from folders local to the project...making it much easier to keep reusable classes you create in one location for easy maintenance, while still allowing any project you make to draw from that code and compile it into its own end product.

It can even optionally do basic minimizing of your code right as you compile it.

I’ve refined it into an extremely useful tool that makes large projects with large codebases much easier to work with all while still coding in actual JS...just surrounded by a package { ... } block and with a couple imports at the top.

 

— Downloads removed, see top of page. —

 


How Do I Use It?

PackageJS started as a personal usage project without any intention of being made public, and I’m on a Windows environment...so the implementation is specific to that environment (in .hta file form). So people on environments that don’t run .hta files won’t get much use from this unless it gets popular enough to warrant me making cross-OS versions. But for those windows users like me...keep reading...

The basics are that for each project you plan to undertake you make a copy of the PackageJS-Compiler.hta file to wherever you’re gonna build your project and set some vars at the top of the file (like telling it what folders to look in for your files, what class is your “base” class that starts everything running, and where to output the compiled end-product file). Then you use the extension .class.js on the files that are intended as PackageJS classes and you’re good to go. You just double click the .hta file to open it and then the interface lets you compile and gives you feedback.


A Build Example

Set Up the Compiler

Let’s make a simple “hello world” type thing. First off figure out where you’re gonna put your project. For example purposes I’ll just use a “MyProject” folder on the desktop. In there I’ll copy/paste a copy of the PackageJS-Compiler.hta file. I’ll then open it up in a text editor to set up my project. When I do so I’ll find some JS variables at the top that look like this:

var classMain = 'pckg.Main';
var codePackages = ['./src-files/', 'D:/JS-FILE-LIB/'];
var output = './compiled-script.js';

var autorun = true;
var staticAuto = true;
var strictMode = true;

There will be some comments after each one explaining them but I didn’t include them here since I’m going to be explaining them anyway.

1) The first one to understand is the codePackages variable (actually the 2nd variable there). That’s an array of places the compiler will look for any class files or other JS files you import or otherwise tell it to use. I have one location that is local to the project I’m working on, and one that is a universal place where I keep a bunch of reusable classes (i.e. my library of code I use). Within those locations it treats packages like folder paths for the class name (i.e. a class MyClass with the package my.name.space would be expected to be found in my/name/space/MyClass.class.js in one of the locations you specify via the codePackages variable.

2) That leads us to the actual first var in the file: classMain. This is where you tell it the “base” class that it will treat as the entry point to the program. So in our example it will look for the “base” class as being file Main.class.js (remember our class files use the extension .class.js) in the folder pckg in one of the locations defined by codePackages. Since it’s a class specific to our project (as opposed to a reusable class we’re keeping in our library for every project to pull from) we’d probably put it in the location specified next to our actual project (i.e. the ./src-files folder right here in our project folder).

3) Next is the output var. This is where it will compile that class and any classes/JS files it imports/includes (and anything they, in turn, import/include) into one file.

4) & 5) The next is the autorun. This should be true for 99% of usages. It basically means it will try to automatically instantiate an instance of the classMain class in order to start the program running. By contrast if you also make the staticAuto true (i.e. both of them are true) then it will still try to run automatically but will try to call a static method .main() on that class rather than making a new instance of it. That’s just two ways to start off running the code: make a new instance of the base class or try to call a static method on the base class.

6) strictMode is pretty easy: if true it will make the entire output file strict mode with a declaration at the top. Even if false you can still use strict mode inside functions in your own code, it just won’t be strict mode globally. If you’re gonna use ES6 classes this pretty much has to be true.


Make a Class File

So at this point we have our compiler set up. Now we can write code. We told it that in one of the 2 locations to look for code it would find the file pckg/Main.class.js, and we told it we would have a src-files folder in our project where we keep files. So let’s make that src-files folder in our MyProject folder and let’s make a pckg folder in that, and a Main.class.js file in that. This class and anything it imports/includes (and anything those files import/include) will be part of our compiled end result. Furthermore since I told it to autorun and staticAuto it’ll be looking for a static Main.main() function in that file to call to start off the code. So we’ll make that file look like this:

package pckg
{
    var Main = {
        main:function()
        {
            alert('hello world!');
        }
    };
}

Notice that that’s perfectly normal JS code making an object type “class” Main with static method .main(), only wrapped in a package statement (or in more prototypal terms, an object Main with a property .main which has a function expression value).


Import, Include, and Using Statements

Any PackageJS style “classes” or normal JS files you wanted to import/include you could do so before the declaration of your class:

package pckg
{
    import my.package.ClassName; // imports another PackageJS (.class.js extension) class
    include "myjsfiles/easeljs.min.js"; // includes a normal JS file
    using createjs.Stage; // optionally shortcut a namespace from a normal JS file
    
    var Main = {
        main:function()
        {
            var myObj = new ClassName(); // we can use our imports!
            var stg = new Stage(); // and our includes! (no namespace cuz of using statement)
            alert('hello world!');
        }
    };
}

Notice in both cases that it’s completely normal JS code aside from the package and the imports/includes.


Ways to Build a Class

You may be aware that there’s many ways to make a “class” in JS, and PackageJS tries to accomodate them all. It will consider any of the following formats to be the end of the import/include area and the start of the class itself:

package my.namespace.here
{
    var Blah = { ...

    // or

    function Blah( ...
    
    // or
    
    var Blah = function( ...
    
    // or

    class Blah ...
}

Be aware that your “class” name (whether it be an ES6 class, an object with static methods/properties, a function declaration, or a function expression) needs to match your filename (i.e. your “class” name being MyClass your filename needs to be MyClass.class.js) and your package needs to match that file’s relative location in the folder where the project is being told to look for code (i.e. package my.namespace needs to be in the folder location my/namespace/). This is basically the way ES4 did things, and the way other package/import type languages do things. It’s how code organization works so well in these systems.

Also know that beyond the above examples of the different “class” code beginnings is literally where the compiler stops messing with/having expectations of your JS code at all, and just compiles it verbatim after that point. For example, if making an object be your “class” as long as it starts with var ClassName = { any code after that is up to you (it’s not really picky about whitespace/line breaks either, for example you can go down a line with that opening curly brace). It only needs that much to confirm the class name/beginning is valid and know that the import/include/whatever section is done. So after that how you populate that object is up to you...whether you actualy use object literal construction to build props/methods, or just close the object up (i.e. var ClassName = {};) and add what you want via dot notation...or whatever. Heck, add a few local-only functions at the end that aren’t even part of that “class” and that’s fine...the compiler don’t mess with code after the “class” beginning. Basically beyond the opening line with the class name (as shown for each of the types in the examples above) it just trusts that the developer knows what they’re doing.


The “as” Keyword

Another neat thing about PackageJS is the ability to handle class name conflicts. For example, there might be two engines you’re using that both have a class Stage, just with different namespacing. You import them both into one of your classes, (i.e. something like import namespace.Stage; and import other.something.Stage;) ...which one is instantiated when you use new Stage(); within that class?

Of course, you could just spell out the whole namespace each time you utilize that class to be sure of which one you’re using, but PackageJS has a better solution: the as keyword. Any import or using statement can have as OtherName tacked onto the end to make it not automatically make shortcut access to the class via only its classname, but instead via your other specified name. Example:

package pckg
{
    import namespace.Stage; // will just be "Stage", since no "as"
    import other.something.Stage as OtherStage; // will be "OtherStage"
    include "myjsfiles/easeljs.min.js";
    using createjs.Stage as EaselStage; // will be "EaselStage"
    
    function MyClass()
    {
        var first = new Stage();      // same as "new namespace.Stage();"
        var secnd = new OtherStage(); // same as "new other.something.Stage();"
        var third = new EaselStage(); // same as "new createjs.Stage();"
    }
}

Other Info

The current version of PackageJS is 2.5.0, and is considered fairly stable as I’ve given it some heavy use.

License for using it is MIT, basically just use it free, I don’t care. Modify it for your own usage all you want too.

For distributing/forking the license is a little different. I want this to stay 1 uniform spec/language/compiler that is well defined enough that 2 completely different people could exchange .class.js files with each other no problem and have the PackageJS compiler not throw them any curves. In order to do that I need to essentially maintain control over it. If you have any contributions to make to it...suggest them to me. If you want to promote it: send them to my page for downloads. That way we know everyone is using the same thing. So basically: its usage is totally open...but its public modification/distribution I claim sole rights to simply so that I can prevent a bunch of different modifications popping up. I have my doubts that this will ever get widespread usage...but if it ever does get a small community of users it would be really nice if there weren’t 12 different distributions of it that they’re trying to account for. Enjoy!