Skip to content
This repository was archived by the owner on Jun 21, 2025. It is now read-only.

Releases: papablack/rws-client

2.9.11

Choose a tag to compare

@papablack papablack released this 16 Jul 11:54

Summary

Stuff done to RWS this time:

  1. fix of HTML watcher issue.
  2. fix of scss imported files watcher issue.
  3. @externalAttr
  4. @externalObservable
  5. @Ngattr upgrade
  6. externalChanged(attrName: string, oldVal: any, newVal: any) fired after changes in props with decorators above
  7. Dev mode linking fix

Type of change

  • Bug fix 🐛
  • New feature ✨
  • Breaking change 💥
  • Project maintenance 🔧
  • Documentation 📖

Description

New version of RWs containing general fixes for dev mode and new @decorators.

Details of decorators usage are in README in section "RWS Decorators".

Multiline decorators fix + debugPackaging option

Choose a tag to compare

@papablack papablack released this 21 May 15:00

@RWSView now operates on multiline decorator usage style

@RWSView('calendar2', { 
  fastElementOptions: {mode: 'open'} 
})
class Calendar2 extends RWSViewComponent {
  connectedCallback () {
  (...)

debugPackaging: true in @RWSView

Important

With this decorator option RWS builder will create component.debug.ts file next to the component class file - it is a preview of what code webpack RWS TS loader creates, before transpiling it into JS

@RWSView('calendar2', { 
  debugPackaging: true
})
class Calendar2 extends RWSViewComponent {
  connectedCallback () {
  (...)

the created file:

import * as T from '@microsoft/fast-element';

import './template.html';            
let rwsTemplate:any = null;

async function handleError(error: Error | any) {      
  const errorMessage = `RWS HTML Error:
${error.stack}`;
  console.error('RWS HTML error', errorMessage);      
  return T.html`<div class="rws-error"><h1>RWS HTML template error</h1>${errorMessage}</div>`;
}

try {        
  //@ts-ignore
  rwsTemplate = 
T.html`
    <section>  <div class="box calendar">    <div class="calendar__header">      <div class="box__title flex">        <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24">          <path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h168q13-36 43.5-58t68.5-22q38 0 68.5 22t43.5 58h168q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm80-80h280v-80H280v80Zm0-160h400v-80H280v80Zm0-160h400v-80H280v80Zm200-190q13 0 21.5-8.5T510-820q0-13-8.5-21.5T480-850q-13 0-21.5 8.5T450-820q0 13 8.5 21.5T480-790ZM200-200v-560 560Z"/>        </svg>        Assignments      </div>      <div class="calendar__header--link">        View Calendar      </div>    </div>    <ul class="calendar__list due">      ${T.repeat(x => x.due_dates, T.html`        <li class="due__margin">          <div class="due__header">            <div class="due__thin">              <span class="due__bold">${x => x.due}</span>              (${x => x.assignments.length})            </div>            <div class="due__header--flex">              <div class="due__header--optional">                Submitted              </div>              <div class="due__header--optional">                Avg Score              </div>            </div>        </div>        <ul class="calendar__list">          ${T.repeat(x => x.assignments, T.html`            <li class="due__assignment assignment">              <div class="assignment__date">                <span class="font--bold">                  ${x => x.due_date}                </span>                ${x => x.due_time}              </div>              <div class="assignment__flex--col">                <div class="assignment__flex">                  <div class="assignment__title">                    ${x => x.title}                  </div>                  <div class="due__header--flex">                    <div class="assignment__all">                      ${x => x.submitted} / ${x => x.total}                    </div>                    <div class="assignment__score">                      ${x => x.avg_score}%                    </div>                  </div>                </div>                <a class="assignment__quiz" href="${x => x.link}">                  ${x => x.quiz}                </a>              </div>            </li>          `)}        </ul>      </li>`)}    </ul>  </div>  </section>`;
} catch (error: Error | any) {
  rwsTemplate = handleError(error);
}
                
import styles from './styles/layout.scss';
const mode = "open";

import { observable, RWSViewComponent, RWSView } from "@rws-framework/client";

type TaskType = {
  [key: string]: any
};

type CallbackType = (data: { data: TaskType[]; }) => void;

@RWSView('calendar2', null, { template: rwsTemplate, styles, options: {mode} })
class Calendar2 extends RWSViewComponent  {

Plugin system + router & websocket package extraction

Choose a tag to compare

@papablack papablack released this 21 May 08:06

RWS Plugin system

Plugin systems lets users add functionality without writing too much code. Target of the functionality is to run with one config line per plugin.

Table of contents

  1. TS work part
  2. JS build plugin part
  3. Adding plugin to TS
  4. Adding plugin to build order
  5. Examples

Currently there are separate frontend and build plugin parts. To be included in .rws.json

1. TS work plugin part

src/plugin.ts

import { RWSClient, RWSClientInstance, RWSPlugin, DefaultRWSPluginOptionsType, NotifyService, NotifyServiceInstance, ConfigService, ConfigServiceInstance } from '@rws-framework/client';
import WSService, { WSServiceInstance, WSEvent, WSStatus } from './services/WSService';

WSService;

interface WSOptions extends DefaultRWSPluginOptionsType {

}

class RWSWebsocketsPlugin extends RWSPlugin<WSOptions> {
    async onClientStart(): Promise<void> 
    {       
        const wsService: WSServiceInstance = this.container.get(WSService);
        const notifyService: NotifyServiceInstance = this.container.get(NotifyService);
        const appConfig: ConfigServiceInstance = this.container.get(ConfigService);

        wsService.on('ws:disconnected', (instance, params) => {
            notifyService.notify(`Your websocket client disconnected from the server. Your ID was <strong>${params.socketId}</strong>`, 'error');
        });

        wsService.on('ws:connected', (instance, params) => {
            notifyService.notify('You are connected to websocket. Your ID is: <strong>' + instance.socket().id + '</strong>', 'info');
        });

        wsService.on('ws:reconnect', (instance, params) => {
            console.info('WS RECONNECTION ' + (params.reconnects + 1));
            notifyService.notify('Your websocket client has tried to reconnect to server. Attempt #' + (params.reconnects+1), 'warning');
        });  

        wsService.init(appConfig.get('wsUrl'), appConfig.get('user'), appConfig.get('transports'));        
    };
}

export { RWSWebsocketsPlugin, WSService, WSServiceInstance, WSEvent, WSStatus };

2. JS build plugin part

./build.js

const {RWSPluginBuilder} = require('@rws-framework/client/src/plugins/_builder');
class BrowserRouterBuilder extends RWSPluginBuilder{
    constructor(buildConfigurator, baseBuildConfig){
        super(__dirname, buildConfigurator, baseBuildConfig);
    }

    async onComponentsLocated(partedComponentsLocations = []){       
        if(!partedComponentsLocations){
            partedComponentsLocations = [];
        } 
        return [...partedComponentsLocations,`${this.pluginPath}/src/components`];
    }

    async onServicesLocated(servicesLocations){        
        return [...servicesLocations,`${this.pluginPath}/src/services`];
    }
    
    async onBuild(buildOptions){     
        this.log('webpack build modified');       
        return buildOptions;
    }
}

module.exports = BrowserRouterBuilder;

Adding plugin to TS

import RWSClient, { RWSContainer, RWSPlugin, RWSPluginEntry } from '@rws-framework/client';
import { RWSBrowserRouter, BrowserRouterOpts } from '@rws-framework/browser-router';

import './styles/main.scss';

import routes from './routing/routes';

import backendImports from './backendImport';
import notifierMethod from './_notifier';
import _initComponents from './application/_initComponents';
import { loadRWSRichWindow } from '@rws-framework/client/src/interfaces/RWSWindow';


async function initializeApp() {
    const theClient = RWSContainer().get(RWSClient);

   //(...)
   
   theClient.onInit(async () => {
        console.log(loadRWSRichWindow().RWS);
        RWSPlugin.getPlugin<RWSBrowserRouter>(RWSBrowserRouter).addRoutes(routes);
        _initComponents(theClient.appConfig.get('parted'));
    }); 

    theClient.setup({  
        partedPrefix: 'rws',  
        partedDirUrlPrefix: '/js',
        parted: true,
        plugins: [ // RWS Plugins option for TS
            RWSBrowserRouter //Plugin class name from package
        ]
    }); 
}

initializeApp();

3. Adding plugin to build order

webpack.config.ts

const path = require('path');

const RWSWebpackWrapper  = require('@rws-framework/client/rws.webpack.config');
const executionDir = process.cwd();

module.exports = RWSWebpackWrapper({
  dev: false,
  hot: false,
  report: false,
  tsConfigPath: executionDir + '/tsconfig.json',
  entry: `${executionDir}/src/index.ts`,
  executionDir: executionDir,
  publicDir:  path.resolve(executionDir, 'public'),
  outputDir:  path.resolve(executionDir, 'public', 'js'),
  outputFileName: 'warlock.client.js',  
  parted: true,
  partedDirUrlPrefix: '/js',
  copyAssets: {
    './public/js/' : [      
      './src/styles/compiled/main.css'
    ]
  },
  rwsPlugins: [
    '@rws-framework/nest-interconnectors',
    '@rws-framework/browser-router'
  ]
});

4. Examples

Example plugin package.json

{
    "name": "@rws-framework/some-plugin",
    "version": "0.2.0",
    "private": false,
    "description": "",
    "main": "src/plugin.ts",
    "author": "papablack"
}

Caution

EVERY plugin needs plugin-info.json

Example plugin plugin-info.json

{
    "name": "some-plugin"
}

Example plugin:

https://github.com/papablack/rws-browser-router

General fixes:

  • @RWSView swaps templates, styles and tag names for WebComponents (STATIC.definition) instead of webpack loader.

2.6.2

Choose a tag to compare

@papablack papablack released this 09 May 14:02

Minor fixes

  • tsconfig file builder fix for linked dev packages
  • small type fixes in decorators file

Full mode pallete RWS build + RWS services for legacy

Choose a tag to compare

@papablack papablack released this 07 May 19:16

RWS 2.6 Release.

New features:

  • DI for legacy
  • Prod build for parted mode

Description

Example .rws.json (placed in build command execution directory):

{
    "dev": true,
    "parted" : true
}

this one will set your components build and load to parted mode and will produce unminified build code with maps to TS files.

A default service can be used in legacy like this:

window.RWS.client.get('ApiService').post('...');

to allow your custom service to be used in window assigned client do this in your service class:

import { RWSService } from '@rws-framework/client';

class NameService extends RWSService {
    static _IN_CLIENT: boolean = true //If set engine will let legacy use the service through RWSClient.get method
    hello(){
      alert('Hello');
    }
    //(...)
}

export default NameService.getSingleton();
export { NameService as NameServiceInstance }

then:

window.RWS.client.get('NameService').hello();

Build modes

Build modes work is complete.

Examples of build for monolith and parted mode:

{parted: false}:

monolith

{parted: true}:

parted_bck

Automatically added by client.js

parted_front

Examples of calendar component in prod and dev modes:

{dev: false}:

calendar2_prod

{dev: true}:

calendar2_dev

2.4.0

Choose a tag to compare

@papablack papablack released this 23 Apr 16:29

DEPENDENCY INJECTION UPDATE

New constructors:

/*
  Minimal import for Service 

    - Default export is FAST DI interface pointing to created singleton service. 
    - Named one returns service class type.
*/
import RoutingService, { RoutingServiceInstance } from '../../services/RoutingService'; 


@RWSView('rws-router')
export class RouterComponent extends RWSViewComponent {    
    static autoLoadFastElement = false;
    private routing: RWSRouter;
    private currentComponent: IRWSViewComponent;

    @observable currentUrl: string;
    @observable childComponents: HTMLElement[] = [];    
    slotEl: HTMLElement = null;

    constructor(@RWSInject(RoutingService) protected routingService: RoutingServiceInstance){
        super();
    }

    connectedCallback() {
        super.connectedCallback();   
           
        this.routing = this.routingService.apply(this);   
                
        if(this.currentUrl){            
            this.handleRoute(this.routing.handleRoute(this.currentUrl));      
        }           
    }
    // (...)
}

Example service

class RoutingService extends TheService {
    private router: Router<any>;
    private routes: IFrontRoutes;

    constructor(@UtilsService private utilsService: UtilsServiceInstance, @ConfigService private config: ConfigServiceInstance){
        super();        
    }    

// (...)
}

export default RoutingService.getSingleton(); // Fast pointer to DI container slot the service instance is in
export { RoutingService as RoutingServiceInstance } // service class type for typing props.

.rws.json

Now we can set env settings for vars from client.setup() / client.start() AND webpack in .rws.json (placed in build command dir)

example config file:

{
  "dev": true,
  "parted": false,
  "partedDirUrlPrefix": "/js",
  "backendUrl": "http://localhost:1337",
  "wsUrl" : "http://localhost:1338",
  "partedPrefix": "jtrainer",
  "copyAssets": {
      "./public/js/" : [
        "./build/",
        "./src/styles/compiled/main.css"
      ]
  }
}

default cfg file: https://github.com/papablack/rws-client/blob/master/cfg/_default.cfg.js

Parted standalone ready.

Choose a tag to compare

@papablack papablack released this 16 Mar 07:02

Ready for rws-project-creator.

Async webcomponents with custom defining tools.

Async web components load as partitioned output.

Choose a tag to compare

@papablack papablack released this 15 Mar 19:01

Breaking changes

Old services:

import {RWSInject, ApiService, ApiServiceInstance, RWSView} from 'rws-js-client

@RWSView('your-tag')
class YourComponent extends RWSViewComponent {
    someMethod(url: string): void
    {
        ApiService.get(url);
    }
}

New (default service):

import { RWSViewComponent, RWSView } from 'rws-js-client

@RWSView('your-tag')
class YourComponent extends RWSViewComponent {
    someMethod(url: string): void
    {
        this.apiService.get(url);
    }
}
 

Default services: https://github.com/papablack/rws-client/blob/7d16d9c6d83c81c9fe470eb0f507756bc6c71b35/src/components/_component.ts#L58

New (custom service):

import { 
    NotifyService, RWSView, RWSViewComponent, 
    WSService, ApiService, ConfigService, 
    ConfigServiceInstance, UtilsServiceInstance, ApiServiceInstance, 
    UtilsService, DOMServiceInstance, DOMService, 
    NotifyServiceInstance, WSServiceInstance, RoutingService, 
    RoutingServiceInstance, RWSInject
} from 'rws-js-client';

import DateService, {DateServiceInstance} from '../../my-custom-services/DateService';


@RWSView('your-tag')
class YourComponent extends RWSViewComponent {
    constructor(
        @RWSInject(DateService) protected dateService: DateServiceInstance,
        @RWSInject(ConfigService) protected config: ConfigServiceInstance,
        @RWSInject(RoutingService) protected routingService: RoutingServiceInstance,
        @RWSInject(DOMService) protected domService: DOMServiceInstance,
        @RWSInject(UtilsService) protected utilsService: UtilsServiceInstance,
        @RWSInject(ApiService) protected apiService: ApiServiceInstance,
        @RWSInject(WSService) protected wsService: WSServiceInstance,
        @RWSInject(NotifyService) protected notifyService: NotifyServiceInstance
    ) {
        super(config, routingService, domService, utilsService, apiService, wsService, notifyService);
        applyConstructor(this);   //fix-incoming: DI constructor data inheritance problem - applyConstructor in super is bugged. Need tmp workaround.
    }

    someMethod(url: string): void
    {
        this.dateService.get(url);
    }
}