@bundles/core

0.9.4 • Public • Published

Bundles Core

NPM version NPM downloads per month Travis branch Codacy code grade Codacy code coverage Coverage Status JavaScript Style Guide code style: prettier semantic release Commitizen friendly License Greenkeeper badge

Bundles is a file bundler for anything. Bundles works similar to WebPack, RollupJS, Parcel, PostCSS except that while other bundlers are designed to compile to a specific output type (like JS, CSS, etc.), Bundles can compile from anything, to anything! Bundles has no assumptions and places no limitations on the type of input it accepts and outputs to; it processes your data however you tell it to. Use Bundles to compile anything your heart desires (and can dream up).

Environment support

Node CLI ES Module Browser UMD
x x x

Terminology

For clarity's sake, the following terms are used as follows:

  • Bundles (capitalized): The core package / tool for Bundles. Or the global Bundles Object, which is parsed from user configuration and then becomes the compiled result.
  • bundles: Configured and/or compiled bundles. More specifically, the bundles property of the global Bundles configuration Object.
  • bundle (noun): A single or specific bundle in Bundles.bundles.
  • bundle (verb): The process of taking one or more user-configured bundles and compiling them -- through a series of bundlers -- to achieve a desired output.
  • bundler: Plugins that make Bundles work. Each bundler is a simple JavaScript plugin or function that compiles/processes Bundles input however you like. Without bundlers, all Bundles does is outputs source input. With bundlers, Bundles can output just about anything you want.
  • config: The global Bundles configuration Object, which consists of Bundles.options, Bundles.data, and Bundles.bundles.
  • options: The options Object, which can be set on the global (Bundles.options) or regional (bundle.options) level. Bundles merges global Bundles.options with each bundle.options.
  • data: The data Object, which can be be set on the global (Bundles.data), regional (bundle.data), or local (file.data, or front matter) levels. Bundles merges global data with each bundle.data, which are each merged with each file.data (front matter).

Install

Install globally:

npm install @bundles/core -g

or locally:

npm install @bundles/core -D

Basic usage

CLI

See the full list of configuration options for configuring Bundles.

# With --config:
bundles --config=<path/to/config.js> [options]
# Or with <input> and --bundlers:
bundle <input>... --bundlers='[...]' [options]

Note: bundle is an alias to the bundles command. Either may be use interchangeably.

NodeJS

See the Bundles Node API for a full list of Bundles' methods.

const Bundles = require('@bundles/core');
Bundles.run(config);

Configuring Bundles

Using a config file

A config file is the recommended way to configure Bundles. There are many ways to structure a Bundles config file.

Single bundle

If you only have a single bundle to configure, you may export a single bundle Object (just make sure it has input and bundlers properties).

module.exports = {
    id: 'my-bundle',
    input: [...],
    bundlers: [...],
    options: {...},
    data: {...}
}

Multiple bundles

For multiple bundles, you may export an Array of bundle Objects.

module.exports = [{...}, {...}, {...}]

Or you may export an Object dictionary of bundles. In this case each key will become the bundle.id.

module.exports = {
    'bundle1': {...},
    'bundle2': {...},
    'bundle3': {...},
}

Global config Object

To easily share global data and/or options between bundles, you may export a global config Object, which has bundles (required), options, on, and data properties. The options and data Objects will be merged with each bundle in bundles (Existing bundle.options and bundle.data will override global configuration). The bundles property can be an Object or Array.

module.exports = {
    bundles: [{...}, {...}, {...}],
    // bundles: {...},
    options: {},
    data: {},
    on: {}
}

Configuring individual bundles

Individual bundles can be configured in a config file/Object as outlined above. Configured bundles become Bundles.bundles, which is an Array of bundle Objects. Each bundle should be configured as follows:

  • id {String} The ID of the bundle, which can be used in other options. If this is not set, it will be the index value (as a String) of the order the bundle was configured.
  • input {String|[String]|Object|[Object]} (required) Source input files. Each entry can be a String or Object. Strings are file or directory paths, globs accepted. An Object is a single file where file.content is a String or Buffer that represent the file's content, useful for passing a file's content directly. File Objects should also have file.path, the file's source path, which will likely be used when compiled by bundlers.
  • bundlers {[String]|[Function]|[Object]} (required) Array of bundlers. A String is a path to a node module, whereas a Function is the bundler function itself. An Object allows you to pass configuration to each bundler. bundler.run is the only required property, and can also be a String or Function. Learn about authoring bundlers.
  • options {Object} Options for this individual bundle. See configuring options.
  • on {Object} [{}] Dictionary of hooks (callbacks) which allow you to tie into Bundles. See Bundles hooks.
  • data {Object} Data for this individual bundle. This will be merged with each output file in the bundle. See configuring data.

Configuration options

Options can be configured in the global config Object, as well as in individual bundles. With the command line interface, options can also be passed as parameters. The following options are available for configuration.

  • run | --run {Boolean|String|[String]} [true] Determines which bundles -- using bundle.id -- will be compiled at runtime. A true or falsy value will run all bundles. A comma-separated String or an Array of bundle IDs will only run a bundle if its ID is listed. Bundles CLI only accepts a comma-separated String value.
  • watch | --watch {Boolean|String|[String]} [false] Determines which bundles -- using bundle.id -- will be watched at runtime. A true value will watch all bundles. A comma-separated String or an Array of bundle IDs will only watch a bundle if its ID is listed. Bundles CLI only accepts a comma-separated String value.
  • watchFiles {String|[String]} An Array or String (globs accepted) of additional files to watch, when the bundle is being watched. These files do not get compiled, they are only added to the watcher and simply kick off a rebundle if/when any of them change. This is useful, for example, for template partials that are depended on by other source input files in the bundle.
  • cwd | --cwd {String} [process.cwd()] The root or current working directory for input source paths.
  • loglevel | --loglevel {String} ['info'] Level of logging. Can be trace, debug, info, warn, error, or silent.
  • glob | --glob {Object} Options passed to globby. Bundles CLI only accepts a JSON Object.
  • frontMatter | --frontMatter {Object} Options passed to gray matter. Bundles CLI only accepts a JSON Object.
  • chokidar | --chokidar {Object} Options passed to chokidar. Bundles CLI only accepts a JSON Object.

Additional CLI-only options

In addition to the config options listed above, Bundles CLI has the following configuration options available.

  • --config {String|JSON Object} Global Bundles config Object. Can be a String filepath to the config file or a JSON Object. When --config exists, <input> files and the --bundlers flag are ignored.
  • <input>... {String} Input files. Must be used in combination with --bundlers, but will be overridden if --config exists. Example: bundles file.txt dir/**/* my/other/dir --bundlers='[...]' [options].
  • --bundlers {String|JSON Array} Bundlers. Can be a comma-separated String of node modules or a JSON Array. Must be used in combination with <input> files, but will be overridden if --config exists.
  • --data {String|JSON Object} Global data. Can be a String filepath to a node module or a JSON Object.

Configuring hooks

Bundles hooks allow you to tie into the Bundles workflow. Like other global properties, hooks can be configured globally via Bundles.on or per bundle via bundles.on. The following hooks are available.

  • afterBundle {Function} (Bundles) => {} Called each time after all bundles are completed. Does not require a return value. This allows you to do things such as run/reload a development server, etc.

Configuring data

Data can be configured in the global config Object, in individual bundles, or locally in file content using front matter. Front matter is parsed with gray matter, which means front matter can exist in many languages (i.e., YAML, JSON, JS, etc.). All data is merged as follows:

// Local file/front matter data is merged on top of bundle.data,
// which is merged on top of global Bundles.data.
file.data = merge(frontMatter, bundle.data, Bundles.data);

Bundles global Object

The Bundles global Object is returned by all Bundles methods, and is formed as follows.

  • success {Boolean} true if all bundles ran successfully.
  • configFile {String|null} Path to config file, or null if a config file was not used.
  • dataFiles {[String]} Input source paths to the configFile and its children data files, if a config file was used.
  • watchingData {Boolean} true if Bundles is watching config/data files.
  • watcher {Object} If watchingData is true, this contains the chokidar watcher.
  • options {Object} Original global configuration options.
  • on {Object} [{}] Dictionary of hooks (callbacks) which allow you to tie into Bundles. See Bundles hooks.
  • data {Object} Original global data.
  • bundles {[Object]} Compiled bundle Objects.

Compiled bundle Objects

Bundles.bundles is an Array of individual bundle Objects. Each bundle contains the following combination of original user configuration and internally created data properties.

  • id {String} The bundle's ID. If this was not configured by the user it will default to the bundle's index (from the order all bundles were run).
  • input {Map} Map which contains each user-configured input source path as the key, and resolved source paths as its value.
  • output {Map} Map of output files, containing compiled source input. The key for each file is its source path.
  • changed {Map} Map of output files that have been changed or modified since last bundle. The key for each file is its source path.
  • removed {Map} Map of output files that have been removed since last bundle. The key for each file is its source path.
  • bundlers {[Object]} Objects Array of configured bundlers which compiled the source input.
  • options {Object} Original user-configured global options, merged with bundle.options.
  • data {Object} User-configured global data, merged with bundle.data, merged with file.data (front matter).
  • success {Boolean} Whether all bundle.bundlers completed successfully.
  • valid {Boolean} Whether the bundle is a valid/appropriately configured bundle.
  • watching {Boolean} Whether the bundle is watching files. When true, the source input will be rebundled when any of the source files change.
  • watcher {Object} When bundle.watching is true, this will contain the chokidar watcher.

Compiled output file Objects

An Array of output file Objects is returned in Bundles.bundles[n].output. Each output file is a compiled source input file, and is formed as follows.

  • content {String|Buffer} The file's compiled content, which can be a String or Buffer.
  • data {Object} File data, which is the merged result of merge( {}, file.source.data, bundle.data, Bundles.data ).
  • encoding {String} ['utf8'] The file's encoding, either utf8 (default) or binary.
  • isBuffer {Boolean} Whether the file is a Buffer (true) or String (false).
  • source {Object} Information about the input source, containing the following properties:
    • path {String} Input source file path, relative to options.cwd.
    • cwd {String} The configured root directory, copied from options.cwd. The file path is resolved as path.join(cwd, path).
    • content {String|Buffer} Input source content, which can be a String or node Buffer. For utf8 content, front matter and excerpts are removed and assigned to other properties below.
    • data {Object} Local front matter data, parsed by gray matter. Gray matter is flexible enough to understand many types of front matter (see details).

Node API

Bundles.run(config)

  • config {String|Object|[Object]} User configuration. Accepts anything a config file will accept. Also accepts a String path to a config file, or config.bundles can be a String path to a config file.
  • @return {Object} The global Bundles Object.

Run/compile bundles from user configuration. Likely the only method you will need to use.

Bundles.create(config)

  • config {String|Object|[Object]} User configuration. Accepts anything a config file will accept. Also accepts a String path to a config file, or config.bundles can be a String path to a config file.
  • @return {Object} The global Bundles Object.

Parse user configuration and "refresh" the global Bundles Object. This means if configuration already exists in Bundles, it is merged with the new user configuration, leaving in tact the original configuration Objects.

Bundles.bundle(idsToRun)

  • idsToRun {String|String[]} Comma-separated String or Array of bundle IDs to run. A true or falsy value will run all bundles.
  • @return {Object} The global Bundles Object.

Runs bundles configured in Bundles.bundles.

Bundles.reset()

Resets global Bundles Object to its original state. Useful in a long-running process when you need a fresh state.

Authoring a bundler

You want to create your own bundler? Great, it's very easy!! A bundler is a simple Function, wrapped in a Node module, with very little boilerplate.

Here's a simple bundler which appends a new line at the end of each file:

module.exports = (bundle, bundler) => {
    bundle.output.forEach((file) => {
        file.content += '\n';
    });
    // Always return the bundle.
    return bundle;
};

You may return a Promise:

const fs = require('fs');
module.exports = (bundle = {}, bundler = {}) => {
    // Return a promise...
    return new Promise((resolve) => {
        bundle.output.forEach((file) => {
            file.content += '\n';
        });
        // In a Promise, always resolve to the bundle Object.
        return resolve(bundle);
    });
};

Guidelines for authoring bundlers

  1. A bundler must return a Function -- synchronous or asynchronous -- which returns the bundle Object. As illustrated above, the bundle and bundler Objects are passed to this Function. See how these Objects are configured.

  2. Become familiar with file Objects. These contain all the data you typically need to modify. A bundler typically will iterate through the files they wish to modify and always returns the bundle.

  3. The purpose of most bundlers are typically to 1) select the files they wish to modify, and 2) modify them according to its goals. Bundles provides the following Map "sets" of files which can be iterated over:

    • bundle.output: Contains all files.
    • bundle.changed: Only files that have changed since last compile.
    • bundle.removed: Only files that were removed since alst compile.
  4. Each file set above is a JavaScript Map, so you can iterate over them with Map.forEach() method, or lookup and interact with specific files with Map.get(), Map.set(), or any of the other useful Map methods.

  5. When iterating over files, it is strongly recommended to iterate over bundle.changed, not bundle.output. This will give you better performance as it takes advantage of Bundles' incremental bundle feature to only rebundle files that have changed.

Package Sidebar

Install

npm i @bundles/core

Weekly Downloads

5

Version

0.9.4

License

MIT

Unpacked Size

72.2 kB

Total Files

5

Last publish

Collaborators

  • thezimmee