Introduction

Components.js is a dependency injection framework for JavaScript applications.

Object-oriented applications are typically composed of hard-wired component. For example, when programming a Car instance, several Seat components of a specific type can be inserted. When different seats would be required, the source code would have to be changed.

Instead of hard-wiring software components together, dependency injection allows these components to be defined externally, without the programmer having to know the exact component during implementation.

Components.js is a dependency injection framework that allows components to be instantiated and wired together declaratively using semantic configuration files. The advantage of these semantic configuration files is that software components can be uniquely and globally identified using URIs. Configurations can be written in JSON.

This software is aimed at developers who want to build modular and easily configurable and rewireable JavaScript applications. For example, Comunica is a meta query engine for the Web that uses Components.js to wire modules together in a flexible way to achieve easily configurable query engines.

Note

More specifically, configuration files are written in JSON-LD, which is a way to serialize RDF in JSON. In fact, different kinds of RDF serialization can be used, such as Turtle and TriG.

The following image shows an overview of how Components.js works in summary. Given an application and several external components, the application can be instantiated and the components can be dynamically injected using a JSON config file.

Components.js Overview

Quick Start (TypeScript)

1. Install dependencies

Components.js can be installed using npm:

$ npm install componentsjs

Component and module files can be automatically generated using Components-Generator.js:

$ npm install -D componentsjs-generator

2. Mark your package as a Components.js module

package.json:

{
  "name": "my-package",
  "version": "2.3.4",
  "lsd:module": true,
  "main": "index.js",
  "types": "index.d.ts",
  ...
  "scripts": {
    ...
    "build": "npm run build:ts && npm run build:components",
    "build:ts": "tsc",
    "build:components": "componentsjs-generator",
    "prepare": "npm run build",
    ...
  }
}

"lsd:module" will allow Components.js to find your module(s) when they are included from other packages.

The "scripts" entry will make sure that all required component files will be generated when building your package.

The componentsjs-generator will look for your compiled TypeScript files (.d.ts) in the lib/ directory. If you use a different output directory for TypeScript (e.g. dist/), you must pass this to the generator using -s flag (e.g. componentsjs-generator -s dist).

3. Create a configuration file to instantiate our class

Assuming a TypeScript class that is exported from the package:

export class MyClass {
  public readonly name: string;
  constructor(name: string) {
    this.name = name;  
  }
}

config.jsonld:

{
  "@context": [
    "https://linkedsoftwaredependencies.org/bundles/npm/componentsjs/^6.0.0/components/context.jsonld",
    "https://linkedsoftwaredependencies.org/bundles/npm/my-package/^2.0.0/components/context.jsonld"
  ],
  "@id": "urn:my-package:myInstance",
  "@type": "MyClass",
  "name": "John"
}

This configuration is a semantic representation of the instantiation of MyClass with name set to "John".

Note

If you want to import a context within your config file, it is important to always refer to the major version range of a package. For example, if your package is at version 2.3.4, then the context URL must become https://linkedsoftwaredependencies.org/bundles/npm/my-package/^2.0.0/components/context.jsonld.

4. Instantiate from config file

...
import { ComponentsManager } from 'componentsjs';

const manager = await ComponentsManager.build({
  mainModulePath: __dirname, // Path to your npm package's root
});
await manager.configRegistry.register('config.jsonld');
const myInstance = await manager.instantiate('urn:my-package:myInstance');
...

myInstance is an instance of type MyClass, as defined in the config file.

After running npm run build, you can now execute your program.

Quick Start (JavaScript)

1. Install dependencies

Components.js can be installed using npm:

$ npm install componentsjs

2. Define your module and its components

Assuming a JavaScript class that is exported from the package:

export class MyClass {
  public readonly name;
  constructor(name) {
    this.name = name;  
  }
}

module.jsonld:

{
  "@context": [
    "https://linkedsoftwaredependencies.org/bundles/npm/componentsjs/^6.0.0/components/context.jsonld",
    { "ex": "http://example.org/" }
  ],
  "@id": "ex:MyPackage",
  "@type": "Module",
  "requireName": "my-package",
  "components": [
    {
      "@id": "ex:MyPackage/MyClass",
      "@type": "Class",
      "requireElement": "MyClass",
      "parameters": [
        { "@id": "ex:MyPackage/MyClass#name", "unique": true }
      ],
      "constructorArguments": [
        { "@id": "ex:MyPackage/MyClass#name" }
      ]
    }
  ]
}

The npm module my-package exports a class with the name MyClass.

The constructor of MyClass takes a single name argument.

3. Create a configuration file to instantiate our class

config.jsonld:

{
  "@context": [
    "https://linkedsoftwaredependencies.org/bundles/npm/componentsjs/^6.0.0/components/context.jsonld",
    {
      "ex": "http://example.org/",
      "name": "ex:MyPackage/MyClass#name"
    }
  ],
  "@id": "http://example.org/myInstance",
  "@type": "ex:MyPackage/MyClass",
  "name": "John"
}

This configuration is a semantic representation of the instantiation of MyClass with name set to "John".

4. Instantiate from config file

...
import { ComponentsManager } from 'componentsjs';

const manager = await ComponentsManager.build({
  mainModulePath: __dirname, // Path to your npm package's root
});
await manager.configRegistry.register('config.jsonld');
const myInstance = await manager.instantiate('http://example.org/myInstance');
...

myInstance is an instance of type MyClass, as defined in the config file.

Learn more

Example Source

Please refer to the remainder of this documentation for more details on each of these parts.

Note

A full stand-alone version of this example, and all other examples in this documentation be found on a dedicated GitHub repository.

Source

Contribute to this documentation on GitHub.

This documentation itself is instantiable using Components.js!