node package manager
Don’t reinvent the wheel. Reuse code within your team. Create a free org »

socko

SOCKO!

SOCKO! - Hierarchical file weaver. Build Status

Introduction

SOCKO! is a file generator, that copies multiple files in a directory hierarchy to build up its output. While doing that, SOCKO! also can stick cartridges in named sockets to build completely new contents.

How SOCKO! works

SOCKO! knows four verbs: a hierarchy, a node, a socket and a cartridge.

The hierarchy is a bunch of directories following a certain setup. These directories define, how the output is going to be. A node is one point in this hierarchy. A socket is a special file in the hierarchy, that depends on cartridges to built up its content.

Let's take a simple example:

We have three files. One file is a static file, that should simply be copied into the output directory. The other one is a socket-file. It has designated parts of the content, that should be exchanged with the contents of a cartridge-file. The third one is also a static file, that simply demonstrates, how SOCKO! works with subdirectories.

These files should be configured for four different nodes: nodeA, nodeB, nodeC and nodeC1.

So, our basic input directory looks like this:

* input
|
*--* static.js
*--* dynamic.js.socket
|
*--* subdirectory
|  |
*  *--* static.js
|
*--* _socko
   |
   *--* dynamic_content1.cartridge
   *--* dynamic_content2.cartridge
   |
   *--* nodeA
   |  |
   |  *--* dynamic_content1.cartridge
   |  
   *--* nodeB
   |  |
   |  *--* dynamic_content2.cartridge
   |
   *--* nodeC
      |
      *--* nodeC1
         |
         *--* dynamic_content1.cartridge
         *--* dynamic_content2.cartridge
         |
         *--* subdirectory
            |
            *--* static.js

In all cases, we would get the following output:

* static.js
* dynamic.js
|
*--* subdirectory
   |
   * static.js

But the content would depend on which node we selected.

./socko.js generate --input input --output output --node nodeA

If we, for example, select nodeA, we would get the two static files from the basic input directory and dynamic.js with its first content socket from nodeA subdirectory and the second content from the _socko root directory.

./socko.js generate --input input --output output --node nodeB

For nodeB, we would get the two static files from the basic input directory as well. This time the dynamic file would contain the first content socket from the _socko root directory and the second content from the nodeB-directory.

./socko.js generate --input input --output output --node nodeC

For nodeC, the two dynamic content sockets from the _socko root directory would be used. The static files would stay the same.

./socko.js generate --input input --output output --node nodeC:nodeC1

For nodeC:nodeC1, finally, the two dynamic content sockets from the nodeC1- directory would be used and the static file in the subdirectory would also be used from the nodeC1-directory.

If you'd like to play with SOCKO!, you can use the sample directory to test with.

Please note, that currently empty directories in the _socko-metadirectory are not available as nodes. There has to be at least one file in it. We recommend to add a small README-file to show, what the node does.

Socket files

Socket files have the suffix .socket and may contain one or more cartridge inclusion directives.

Because SOCKO! is file type-agnostic, there are different directives from which you can choose, so your file validator still validates the socket files.

XML-Style:

<!-- SOCKO: CARTRIDGE-NAME -->

JSON-Style (the , is optional):

"_SOCKO!": "CARTRIDGE-NAME",

Hash-Comment-Style:

# SOCKO: CARTDRIDGE-NAME #

Slash-Comment-Style:

// SOCKO: CARTDRIDGE-NAME //

Multiline-Slash-Comment-Style:

/* SOCKO: CARTRIDGE-NAME */

Native-Style:

{{<< CARTRIDGE-NAME >>}}

In all cases, the directive has to be in one single line and the cartridge will replace the whole line.

The parameter in the directive directly refers to a cartridge-file name.

For example, this directive:

{{<< content1 >>}}

would look for a cartridge file named content1.cartridge.

Please note, that socket and cartridge files currently have to be UTF-8 encoded!

Cartridge-Collectors

SOCKO! also supports specifying multiple cartridges to insert into a socket file in one line.

For this to work, instead of setting a cartridge name, do something like this:

{{<< COLLECT:SCOPE:TYPE:PATTERN >>}}

If the cartridge name starts with "COLLECT:", SOCKO! understands it as a cartridge collector directive.

This directive has the following parts, separated by a ":".

  • SCOPE: The scope defines how many levels SOCKO! will scan for matching cartridge files. The value 0 only scans the current node. The value 1 will also scan the parent node and so on. Specifying a "-" as the value will scan the complete hierarchy, up to the root node.
  • TYPE: There are different matching types available:
    • R: The parameter PATTERN is a regular expression, that has to match the available cartridge names
    • G: The parameter PATTERN is a glob expression, that has to match the available cartridge names
  • PATTERN: expression, based on the TYPE-parameter

If there are no matches, the directive is simply removed from the output file.

Ignoring cartridges

If you'd like to exclude specific cartridges (and thus leaving the part of the socket file empty), you can add one or more "--ignore" parameters together with cartridge file names.

You can also specify to exclude cartridges in a specific node by prefixing that node with : like this:

node socko.js --ignore nodeA:dynamic_txt_content1 (...)

Directory includes

If you'd like to fill a complete directory with files from a directory inside the "_socko"-Metadirectory, you can do this by creating an empty directory in the input directory and placing the following file inside it:

.socko.include

This file holds a scope (like the cartridge collectors above) and a globbing pattern defining which files should be included:

SCOPE:PATTERN

Example:

-1:**/*

This will include all files and subfolders and also check parent nodes for missing files.

During generation this file will be deleted and instead the selected content from the corresponding directory inside the socko node will be used. If no matching directory inside the socko node exist, the directory is skipped.

Example:

* input
|
*--* _socko
|  |
|  *--* exampleinclude
|  |  |
|  |  *--* file3.json
|  |
|  *--* dev
|     |
|     *--* exampleinclude
|        |
|        *--* subdirectory
|        |  |
|        |  *--* file4.yaml
|        |
|        *--* file1.xml
|        *--* file2.txt
|
*--* exampleinclude
   |
   *--* .socko.include

If .socko.include has this content:

0:**/*

The output will look like this:

* output
|
*--* exampleinclude
   |
   *--* subdirectory
   |  |
   |  *--* file4.yaml
   |
   *--* file1.xml
   *--* file2.txt

If .socko.include has this content:

-1:**/*

The output will look like this:

* output
|
*--* exampleinclude
   |
   *--* subdirectory
   |  |
   |  *--* file4.yaml
   |
   *--* file1.xml
   *--* file2.txt
   *--* file3.json

Renaming files in flight

If you'd like to rename the files created during a SOCKO! run, you can use the --rename argument. This argument can be specified multiple times and should be in the form

--rename source-path:destination-path

The paths should be relative to the input directory. If SOCKO! finds a path, that matches source-path during its run (in all features), it is automatically translated to destination-path.

Skipping recreation of files, that have the same content

Usually, SOCKO will simply flood your output directory with the generated files. It does not check, if the new content is the same as the old content.

If you don't want this for some reason, add the parameter

--skipIdenticalSockets

and SOCKO will check, if the files differ before it actually writes them.

Requirements

  • Node.js
  • seeli
  • winston
  • handlebars
  • merge

Usage

Start socko by issuing

node ./socko.js

Use "help" or "--help" to display the available options and commands.

License

Copyright (c) 2016 Dennis Ploeger Licensed under the MIT license.