Releases: papablack/rws-client
Release list
2.9.11
Summary
Stuff done to RWS this time:
- fix of HTML watcher issue.
- fix of scss imported files watcher issue.
- @externalAttr
- @externalObservable
- @Ngattr upgrade
- externalChanged(attrName: string, oldVal: any, newVal: any) fired after changes in props with decorators above
- 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
@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
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
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
Full mode pallete RWS build + RWS services for legacy
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}:
{parted: true}:
Automatically added by client.js
Examples of calendar component in prod and dev modes:
{dev: false}:
{dev: true}:
2.4.0
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.
Ready for rws-project-creator.
Async webcomponents with custom defining tools.
Async web components load as partitioned output.
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);
}
}




