packman

text file packaging command line utility

View the Project on GitHub captainbrosset/packman

packman is a command line utility used to package text files together.

Even if packman was born of the need to group several JavaScript files together in one big, minified, versioned JavaScript file to improve websites' performance, it works with any type of text files and can package any number of files in any number of crazy ways you want.

Installation

packman can be installed via npm, so first, get nodejs installed. Then type:

npm install -g packman

Usage

packman

Options:
  -c, --config   Path to the config file to use (defaults to .packman in the current working dir)   [default: "./.packman"]
  -l, --logging  Logging level (only errors = 1, also warnings = 2, also info = 3, also debug = 4)  [default: 3]
  -h, --help     Displays this message                                                              [boolean]
  -w, --watch    Watch for file changes and run packman again if any                                [boolean]

As you can see, no option is mandatory. By default, packman will look for a file named .packman in the current directory.

Using the watch mode helps with productivity. It basically lets packman run and watch for file changes in the configured source directory so instead of having to manually run packman every time you make a change, your packages will be created after each and every file changed, automatically.

Config files are written in yaml and look like this

source: my/source/dir
destination: my/target/dir
eraseIfExists: true
visitors:
    - coffeescript
    - jsmin
packages:
    main.js:
        files:
            - CORE.js
            - CORE/Object.js
            - CORE/Logging.js
    utils.js:
        files:
            includes:
                - CORE/utils/**/*.js
            excludes:
                - CORE/utils/specials/*.js
    css/styles.css:
        visitors:
            - less
        files:
            includes:
                - **/*.less

The first level of the config contains general configuration like the source and destination folders, a flag telling packman whether the destination should be erased if it already exists or not, the global visitors to use and the list of packages to create.

Many configuration parameters are optional, in fact, packages is really the one you want to work on. Here is the default config:

source: ./src
destination: ./target
eraseIfExists: false
visitors:
    - sep
packages:

Then, inside the packages node, is the list of all packages to be created.

Files to be included inside a package are given in the files sub-node and can be either an array of file names, or an object with either includes or excludes or both. These sub-properties are arrays of file names or ant pattern (?, *, **). If an array of files is provided, the files will be merged in this order.

Visitors

You may have noticed that the configuration accepts a visitors array. A visitor is a piece of code that transform package content, package name and file content while things are done. This is the way to get packages perfectly fit to your needs.

You can imagine using visitors for pretty much anything you want. Visitors can alter the file content but also package file names and locations or simply output other files.

Examples of visitors include (but are not restricted to) including separators between the files, minifying file content, renaming package files to include a version number, processing less css or coffeescript files.

packman comes with a bunch of built-in visitors:

Using visitors

Visitors can be specified either globally at the top level of the config file, or locally, within each package definition. Visitors are configure through an array, and are, therefore, run in a sequence, one after the other.

Visitors specified simply by name (e.g. "less") is assumed to be a built-in visitor. If you want to use a visitor that is not provided by packman by default, specify the full (relative or absolute) path to the js file.

Developing visitors

A visitor in packman is simply a nodejs module that exports any of the following functions:

// At the very start, just after the config file is loaded
onAfterConfigLoaded: function(callback, config) {},

// Main entry point, before any file is packaged
onStart: function(callback, config) {},

// Before starting to package a set of files together
onPackageStart: function(callback, config, packageFileObject) {},

// Before a file is being inserted into a package
onFileStart: function(callback, config, packageFileObject) {},

// When inserting the content of a file into a package
onFileContent: function(callback, config, fileObject) {},

// After a file has been inserted into a package
onFileEnd: function(callback, config, packageFileObject) {},

// After having packaged a set of files together
onPackageEnd: function(callback, config, packageFileObject) {},

// At the end, when all packages are done
onEnd: function(callback, config) {}

Visitors' methods can be asynchronous if needed, this is why they accept a callback as their first parameter. Once their processing is done, they must call the callback to allow packman to continue looping on other visitors, and ultimately on other files and packages.

Note that since the config is passed as argument to the above methods, you can add extra data to it to be used by visitors.

Most visitors' methods accept a packageFileObject as argument while the onFileContent method accepts a fileObject argument, here are their interfaces:

PackageFile = {
    path: "the logical path of the package file",
    content: "the current content of the package file",
    currentFile: "reference to the File instance that is currently being packaged if any"
};

File =  {
    path: "the logical path of the file",
    physicalPath: "the physical path of the file",
    content: "the current content of the file",
    packageFile: "reference to the PackageFile including this file"
};

Most visitors will want to modify the fileObject.content on the fly (to minify javascript for instance) or to append content to the packageFileObject.content (to insert separators for instance).

Special configuration parameters

A couple of other parameters are more seldomly used but could be interesting to you