1.0.2 • Published 5 years ago

clusterjs-cli v1.0.2

Weekly downloads
-
License
ISC
Repository
-
Last release
5 years ago

ClusterJS-CLI

This module represents a CLI and a framework. It's purpose is to help you to create the configuration files and then to help you easily create folders and files according to this framework specifications. This framework represents a collection of webpack configuration and loaders which allow you to modularize your code.

This is a personal project for personal use, thus it may not be made for production ready, so use it only if you feel confident enough.

Requirments

Node version >=8.11.4

Install

npm i -g clusterjs-cli

Commands

You can see all the commands by running clusterjs-cli --help

1.Create new app

clusterjs-cli -n [appName] - creates a new app named appName.

Then run npm install or npm i to install all the modules

Start the application

Inside package.json are 5 commands.

npm run build:prod - will build the aplication in the production mode

npm run build:dev - will build the application in the development mode

npm run start:dev - will fire the application in the development mode

npm run start:prod - will fire the application in production mode

npm run serve - will create a http server and it will use as static files the folder dist ! It requires http-server module to be globally installed

! Default the app port will be 5050. You can change it inside webpack.config.js

2.Create a cluster

A cluster is a delimitation of your UI. The interface can be one or more clusters. Each cluster can have one more views.

clusterjs-cli -c [clusterName] - create a new cluster named clusterName

This will create a new folder named clusterName with a folder named views and a file with the same name clusterName.ts inside clusters folder. clousterName.ts will be the initializer for this HTML element. In the same time it will update the router.ts file and the

! This command should be runned inside the root folder, because under the hood this command it will search for the src folder. If no src folder will be found, then it will throw an error assuming that you are not running the command in the root level.

This command will do the following changes:

1.Update router.ts

_[clusterNameCamelCase]_: {
 setView: async (view: string) => {
       /**
        * * 1. Set the cluster status as "loading-view" in order to do the afferent logic for this action
        * * 2. Load the the view as a chunk (to better separate code and components)
        * * 3. Call initView method in order to initialize this view
        * * 4. Remove "loading-view" attr from the cluster
        *
        */
      // * 1
      document.querySelector("my-cluster").setAttribute("loading-view", "");
      // * 2
      const {
        default: _
      } = await import(/* webpackChunkName: "view" */ `@Clusters/my-cluster/views/${view}/${view}`);
      // * 3
      _.initView();
      // * 4
      document.querySelector("my-cluster").removeAttribute("loading-view");
    }
}

2.Update index.pug

body
    my-cluster

3.Update app.ts

/**
 * * Dependencies
 */
import "@webcomponents/custom-elements";
import "@Clusters/my-cluster/my-cluster";

Create a view

! This command must be runed inside the cluster's folder for which you want to create the view.

  1. cd .\src\clusters\[clusterFolderName]
  2. clusterjs-cli -v [viewName] - create a new view with the name viewName

Then to set a view

router._[clusterNameCamelCase]_.setView("_[view-name-kebab-case]_");

This command will do the following changes:

1. Create a template .pug

Here is the template for this view and here will be the components included.

2. Create a stylesheet .scss

The style will be placed inside

:local {
}

3. Create the controller .ts

/**
 * * Dependencies
 * * 1. Style for this view
 * * 2. Template for this view
 * * 3. Data to interpolate
 * * 4. Add the css classes
 */

const style: any = require("./my-view.scss"); // * 1
const template: any = require("./my-view.pug"); // * 2
const locals: any = { myViewData: "My Demo data for this view" }; // * 3
Object.keys(style).forEach(key => (locals[key] = style[key])); // * 4


/**
 * * Components
 */


/**
 * * Initiate this view
 * @param data - data to insert in this view
 */
function initView(data: any) {
  /**
   * * 0. Interpolate the received data if any
   * * 1. Insert it inside the cluster parent
   */

  // TODO: Interpolate data if any
  // * 0

  // * 1
  document.querySelector("my-cluster").innerHTML = template(locals);
}

export default { initView };

Create a component

! This command must be runed inside the root folder of your project, because it will search for the components folder

  1. clusterjs-cli -p [componentName] - create a new component with the name componentName

Then to add the component in a view

view.ts

import "@Components/[component-name-kebab-case]/component-name-kebab-case";

view.pug - add the component html tag

[my-component-html-selector]

When you have more than one component, use this at the top of your my-component.ts file, to avoing redeclaration error for style,template and locals variables:

import { resolve } from "q";

Component communication

Interpolate custom data before render

You can interpolate custom data from the view to the component before you render it using interpolate attr.

  1. Inside the view template (.pug) set the following my-component(interpolate="Data interpolated from the view, before render")

  2. Inside the component template use a variable with the same name to capture the data the data p(class=`${myComponentClass}`)=interpolate

  3. Inside the component's class set the interpolate attr as observable.

  static get observedAttributes(): string[] {
    return ["interpolate"];
  }
  1. Create a getter and setter for this attr.
 get interpolate(): boolean {
    return this.hasAttribute("interpolate");
  }

  set interpolate(val) {
    if (val) {
      this.setAttribute("interpolate", "");
    } else {
      this.removeAttribute("interpolate");
    }
  }
  1. Use attributeChangedCallback to capture any changes inside the observable attributes.
attributeChangedCallback(
    name: string,
    oldValue: string,
    newValue: string
  ): void {
    /**
     * * 1. Check what attr was changed
     * * 2. Add data to locals
     */
    // * 1
    if (this.interpolate) {
      // * 2
      locals[name] = newValue;
      this.interpolate = false;
    }
  }
  1. Then render the component
connectedCallback() {
    this.innerHTML = template(locals);
  }

FULL CODE

/**
 * * Dependencies
 * * 1. Style for this component
 * * 2. Template for this component
 * * 3. Data to interpolate
 * * 4. Add the css classes
 */
const style: any = require("./my-second-component.scss"); // * 1
const template: any = require("./my-second-component.pug"); // * 2
const locals: any = {}; // * 3
Object.keys(style).forEach(key => (locals[key] = style[key])); // * 4

class MySecondComponent extends HTMLElement {
  constructor() {
    super();
  }

  static get observedAttributes(): string[] {
    return ["interpolate"];
  }

  get interpolate(): boolean {
    return this.hasAttribute("interpolate");
  }

  set interpolate(val) {
    if (val) {
      this.setAttribute("interpolate", "");
    } else {
      this.removeAttribute("interpolate");
    }
  }

  attributeChangedCallback(
    name: string,
    oldValue: string,
    newValue: string
  ): void {
    /**
     * * 1. Check what attr was changed
     * * 2. Add data to locals
     * * 3. Delete the att
     */
    // * 1
    if (this.interpolate) {
      // * 2
      locals[name] = newValue;
      // * 3
      this.interpolate = false;
    }
  }

  connectedCallback() {
    this.innerHTML = template(locals);
  }
}

customElements.define("my-second-component", MySecondComponent);

Transport data from the view to component (down)

This communication is done in the same manner: using an obervable attribute. The content for my-component.ts file is the same. Just replace interpolate with transporter-down.

1.

import { resolve } from "q";

/**
 * * Dependencies
 * * 1. Style for this component
 * * 2. Template for this component
 * * 3. Data to interpolate
 * * 4. Add the css classes
 */
const style: any = require("./my-third-component.scss"); // * 1
const template: any = require("./my-third-component.pug"); // * 2
const locals: any = {}; // * 3
Object.keys(style).forEach(key => (locals[key] = style[key])); // * 4

class MyThirdComponent extends HTMLElement {
  constructor() {
    super();
  }

  static get observedAttributes(): string[] {
    return ["transporter-down"];
  }

  get transporterDown(): boolean {
    return this.hasAttribute("transporter-down");
  }

  set transporterDown(val) {
    if (val) {
      this.setAttribute("transporter-down", "");
    } else {
      this.removeAttribute("transporter-down");
    }
  }

  attributeChangedCallback(
    name: string,
    oldValue: string,
    newValue: string
  ): void {
    /**
     * * 1. Check what attr was changed
     * * 2. Insert data into component
     * * 3. Delete the att
     */
    // * 1
    if (this.transporterDown) {
      // * 2
      this.querySelector(`.${style.myComponentClass}`).innerHTML = newValue;
      // * 3
      this.transporterDown = false;
    }
  }

  connectedCallback() {
    this.innerHTML = template(locals);
  }
}

customElements.define("my-third-component", MyThirdComponent);
  1. After, when you want to send data from the view to component, just set the attr transportere-down on the component's html element and as value the data you want to send.

my-view.ts

document
    .querySelector("my-third-component")
    .setAttribute(
      "transporter-down",
      "Data send from my-view to component after render"
    );

Transport data from the component to view(up)

This communication is done using a custom event.

  1. Create a custom event in my-component.ts with the data you want to send
class MyFourthComponent extends HTMLElement {
  transporterUp: Event;

  constructor() {
    super();
    this.transporterUp = new CustomEvent("transporterUp", {
      detail: {
        data: "data send from component"
      }
    });
  }
  .
  .
  .
  1. Send data

this.dispatchEvent(this.transporterUp);

import { resolve } from "q";

/**
 * * Dependencies
 * * 1. Style for this component
 * * 2. Template for this component
 * * 3. Data to interpolate
 * * 4. Add the css classes
 */
const style: any = require("./my-fourth-component.scss"); // * 1
const template: any = require("./my-fourth-component.pug"); // * 2
const locals: any = {}; // * 3
Object.keys(style).forEach(key => (locals[key] = style[key])); // * 4

class MyFourthComponent extends HTMLElement {
  transporterUp: Event;

  constructor() {
    super();
    // * Create custom event
    this.transporterUp = new CustomEvent("transporterUp", {
      detail: {
        data: "data send from component"
      }
    });
  }

  connectedCallback() {
    this.innerHTML = template(locals);
    this.querySelector("button").addEventListener("click", () => {
      this.dispatchEvent(this.transporterUp);
    });
  }
}

customElements.define("my-fourth-component", MyFourthComponent);
  1. To capture the data, then just add the custom event on the component's html element.

my-view.ts

document
    .querySelector("my-fourth-component")
    .addEventListener("transporterUp", function(e: CustomEvent) {
      console.log(e.detail);
    });

Load env variables

You can write your production and development variables inside one of the .env files. Then to get the in your app all you have to do is: process.env.[myVar]

1.0.2

5 years ago

1.0.1

5 years ago

1.0.0

5 years ago