gobem

0.0.7 • Public • Published

gobem

This npm-module is a builder for SPA BEM projects. It has a lot of options and does the following:

  • scans a root folder to find files describing dependencies (scanner.js);
  • resolves dependencies and forms the list of modules (resolver.js);
  • forms the list of files for each entry point and loads ones (mapper.js);
  • processes files and writes them to the disk (builder.js).

Terminology

Just try to read all the material and construct in your head the vision how it works.

File Structure

What does mean BEM project? It means, that files are located like in the tree below:

root/
- - components/
- - - - w-app/
- - - - - - elem/
- - - - - - - - elem.html
- - - - - - - - elem_color_red.styl
- - - - - - w-app.deps.json
- - - - - - w-app.html
- - - - - - w-app.js
- - - - - - w-app_disabled.js
- - - - - - w-app_size_big.styl
- - scripts/
- - - - jquery/
- - - - - - jquery.js

As you can see the project has the strong file structure:

  • categories (components, scripts);
  • blocks (w-app, jquery);
  • elements (w-app__elem);
  • modifiers (w-app_size_big, w-app__elem_color_red, w-app_disabled).

Filename's Components

Let's analyze a filename components/w-app/elem/elem_color_red:en.deps.json:

  • components is a categorie's name;
  • w-app is a block's name;
  • elem is an elem's name;
  • color is a modifier's name;
  • red is a modifier's value;
  • en is a language;
  • deps.json is a so-called file's technology;
  • json is a file's extension.

All files of the project should be named according with these rules.

Entry and Exit Points

Blocks, which name starts with p- (pages) or w- (wrappers) are called entry points. Its are roots of module's dependencies. Typical names for entry points are p-profile, p-info, w-app or w-landing.

When pages, wrappers and languages intersect each other, we get exit points. Exit points are independent and gobem builds files of each one. Typical names for exit points are components/p-info+components/w-app: or components/w-app+components/w-app:en.

Build process is independent for each exit point! The amount of output files could be calculated as:
(files) = (wrappers) * (pages) * (langs) * (deps) * (techs)
Of course the real number is less, because not all files exist.

Modules and Files

Module is an abstract entity: block, element or modifier. For example this is a module's path: components/w-app/elem.

If gobem has configuration with three languages and three technologies:

config.buildLangs = ['', 'ru', 'en'];
config.buildTechs = ['js', 'css', 'html'];

then 1 module will be mapped with 9 files:

  • components/w-app/elem.js
  • components/w-app/elem.css
  • components/w-app/elem.html
  • components/w-app/elem:ru.js
  • components/w-app/elem:ru.css
  • components/w-app/elem:ru.html
  • components/w-app/elem:en.js
  • components/w-app/elem:en.css
  • components/w-app/elem:en.html

Wrappers, Pages, Singletons, Blocks

The client-side architecture of any well-designed single page application consists of the following block's types:

  • wrapper (prefix w-)
    Constant block, which manages pages inside itself. It loads necessary resources (styles, scripts, templates, etc) and changes the content of page's container. Wrapper is a singleton, loaded only once.
  • page (prefix p-)
    Just a regular block. Any page could be loaded many times.
  • singleton (prefix s-)
    Singleton is a block, loaded only once with wrapper.
  • block (prefix b-)
    Just a regular block, which could be used and loaded as much as you want.
type is singleton is entry point
w- yes yes
p- no yes
s- yes no
b- no no

Configuration

Here are described all configurable parameters for gobem npm-module. Containing them object should be passed to gobem.configure(config).

beforeBuilding( )

This function is called each time before rebuilding. It gets two arguments: next and config. An example given:

config.beforeBuilding = function (next, config) {
    let filePath = path.join(config.rootDir, 'file.txt');
    fs.readFile(filePath, 'utf-8', (error, content) => {
        app.setFileContent(content);
        next(error);
    });
};

afterBuilding( )

This function is the same as beforeBuilding(), but called after each rebuilding.

rootDirabc

Root directory should be an absolute path to the folder, containing project files.

outputDirabc

Output directory should be a relative path to the folder, where resulting files will be saved. By default is equal output.

buildInstructions[ ]

An array of instructions, which will be executed during each rebuilding. Each instruction is an array too, which contains a name of a processor and arguments for a function-constructor. Example given:

config.buildInstructions = [
    ['select', 0, /\.js$/],
    ['write', 1]
];

If you have a complicated project, probably you want to use constructor function instead an array. gobem allows it.

config.buildInstructions = function (exitPointName) {
    return [
        ['select', 0, /\.js$/],
        ['write', 1]
    ];
};

excludePath[ ]

If you want to exclude some folders or files from your project, point this fact here. All paths should be relative. If you have only one file to exclude, string could be passed.

depTechabc

This is a technology of files, which contain exhaustive information about dependencies. By default is deps.json.

buildLangs[ ]

An array of the languages, which should be built. By default is [''] (only not localized files will be built).

buildTechs[ ]

An array of the technologies, which should be built. By default is [] (nothing will be built).

buildLoaders[ ]

An array of the technologies, which should be loaded in the memory. By default is equal to buildTechs (all technologies will be loaded). Not loaded files is processed too, but their content is equal null. You are able to load ones by your preferred way inside a processor.

This feature is needed, for example, to prevent loading *.node.js files (usually we use require to connect npm-modules).

maxExecutionTime123

This number is an upper time limit of the build process (in seconds). Default value is 60 seconds. 0 means there is no limit.

clearOutput!!

This boolean flag says, that gobem should clear previous output, before start writing the current one. Default value is true.

overwriteOutput!!

This boolean flag says, that gobem should overwrite existing files. If true all files are stored to the same folder, if false then files are divided into directories by exit points.

For example:

// overwriteOutput == false
// (each exit point has own files)
output
- - components^w-app+components^w-app:
- - - - components
- - - - scripts
- - components^p-page+components^w-app:
- - - - components

// overwriteOutput == true
// (files of all exit points are merged)
output
- - components
- - scripts

If you use SPDY or HTTP2, it is preferable set overwriteOutput to true. But be careful: if several exit points have files with the same path and different content, it will be available only one version.

rebuildByWatcher!!

Triggers rebuilding, when any project's file have been changed. By default is false.

rebuildByFile[ ]

This is a list of files, where all paths are relative. If a file from the list is changed, rebuilding is triggered. By default it's empty array []. In case with one file could be a string.

For example, you can add in this array build.js (module, which constructs buildInstructions) and rebuild your project immediately after build instructions have been changed.

rebuildByTimer123

The time in seconds between two successful rebuldings. Default value is 0, what means that the project aren't rebuilt by timer.

rebuildByError123

The time in seconds between two attempts of the rebuilding in case of an error. Default value is 20 seconds.

Building

gobem.configure(config{ })

This method configures gobem, runs a timer for rebuilding (if one exists), starts watching the files.

Note that gobem.configure() don't trigger rebuilding. It is recommended to call gobem.build() immediately after configuration.

gobem.build()

Builds a project, using the last configuration. Usually this method is called automatically.

gobem.use(exitPointNameabc)

Returns a promise. Resolve function gets an array with files of the required exit point. If error occured in the build process, promise will be rejected with error gobem.status().

gobem.reset()

Resets gobem to initial state. This method is always called before the configuration.

gobem.status()

Returns an error, that occured in the build process. If a project has built successfully, null will be returned.

Processing

Processor is an entity, which gets one file's map and returns another one. The most of processor's methods have the same arguments:

  • next( )
    All methods are asynchronous. After processing data it needed to call this function (first argument is error).
  • inputMap
    Read-only map, where keys are file's paths and values are file's contents.
input.get('components/w-app/elem/elem.js');
  • outputMap
    Output map is the place, where all results should be stored.
output.set(filePath, '"use strict";\n' + fileContent);
  • config{ }
    Initial gobem config.

processor = require(name)(arg1, arg2, ...)

Each processor is a separate npm-module. This module should export a factory-function, which returns required processor. The factory gets arguments from buildInstructions, it's executed in the special context, which has some additional information. An example given how to create the empty processor:

module.exports = function (arg1, arg2) {
    // this.config - initial config
    // this.modules - all modules of the current project
    // this.exitPoint - the name of the building exit point
    return {};
};

processor.before(next( ), inMap, outMap, config{ })

This method is called before processing (once per exit point).

processor.process(next( ), inMap, outMap, config{ }, contentabc, pathabc)

This method is called for each file of a current exit point. Pay attention, that path always is a string with file's path, but content could be different.

  • Usually it is a string, containing file's content.
  • If file doesn't exist, content is undefined.
  • If file has technology, not specified in buildLoaders, content will be equal null (it means gobem even didn't try to load file).

processor.after(next( ), inMap, outMap, config{ })

This method is called only after successful processing (once per exit point).

processor.clear(next( ), inMap, outMap, config{ })

This method is always called after processing has finished (once per exit point). Its goal to release using resources.

Build Instructions

An array buildInstructions says to gobem how to build each exit point. It means gobem applies the same instructions to each exit point.

Each instruction from this array contains as the first argument the name of the processor, which should be used. Except processors, installed in the directory node_modules, you are able to use some internal processors. Usually they work with virtual storages.

What are virtual storages? 0-storage is read-only, it's source files of exit point. Then gobem copies files from one storage to another, processes ones, clears some storages and finally gets output - resulting files. But what is the output? It is the storage with a maximal id.

Obligatorily see an example in the end of section, how buildInstructions work! Below are described all internal processors of gobem:

select storage123 regexp/.*/

Copies files, whose paths satisfy the regular expression regexp, from the storage to the buffer. By default regexp is .*, that means all files are matched.

write storage123

Writes all files from the buffer to the storage.

clear storage123

Deletes all files of the storage.

debug callback( ) exitPointNameabc

Passes exit point name, iterable keys and values to callback (3 arguments in the sum). If exitPointName is specified, then this processor works only for one exit point.

call callback( )

Calls callback function with a single argument - the name of a current exit point.

// abstract example of an array with build instructions
config.buildInstructions = [
    ['select', 0, /^[^.]+\.lib\.js$/],
    ['gobem-proc-js-minify'],
    ['write', 1],
 
    ['select', 0, /^[^.]+\.es6\.js$/],
    ['gobem-proc-babel'],
    ['write', 1],
 
    ['select', 1],
    ['gobem-proc-wrap-script'],
    ['write', 2],
 
    ['clear', 1],
    ['select', 0, /^[^.]+\.lib\.css$/],
    ['write', 1],
 
    ['select', 0, /^[^.]+\.styl$/],
    ['debug', (exitPointName, keys) => console.log(Array.from(keys)), 'modules/w-app+modules/w-app:'],
    ['gobem-proc-stylus'],
    ['write', 1],
 
    ['select', 1],
    ['gobem-proc-css-minify'],
    ['write', 2],
 
    ['call', function (exitPointName) {
        global.counter++;
    }]
];
// storage 2 is an output

*.deps.json

To describe dependencies between modules, it is used deps.json technology. Below is how to specify dependencies, using this format:

/* w-app.deps.json */
{
    "components": [
        "_size_big",
        "__border",
        "_color_red",
        "b-block",
        "_disabled"
    ],
    "scripts": ["jquery"],
    "services": ["timer", "ajax"]
}

/* will be transformed to the following modules */
components/w-app_size_big
components/w-app/border
components/w-app/border_color_red
components/b-block
components/b-block_disabled
scripts/jquery
services/timer
services/ajax

Usage example

Here is a repository, which can help you to understand, how it works.

Package Sidebar

Install

npm i gobem

Weekly Downloads

1

Version

0.0.7

License

GPL-3.0

Last publish

Collaborators

  • enet